Trong lần đầu tiên hỗ trợ API Web Push, Chrome dựa vào dịch vụ đẩy Giải pháp gửi thông báo qua đám mây của Firebase (FCM), trước đây gọi là dịch vụ Gửi thông báo qua đám mây của Google (GCM). Bạn cần phải sử dụng API độc quyền của ứng dụng này. Điều này cho phép Chrome cung cấp API Web Push cho các nhà phát triển tại thời điểm thông số kỹ thuật của Giao thức đẩy web vẫn đang được viết và sau đó cung cấp tính năng xác thực (nghĩa là người gửi thông báo là người mà họ cho biết) tại thời điểm Giao thức đẩy web thiếu tính năng này. Tin vui là cả hai điều này đều không còn đúng nữa.
FCM / GCM và Chrome hiện hỗ trợ Giao thức đẩy web tiêu chuẩn, trong khi bạn có thể xác thực người gửi bằng cách triển khai VAPID, nghĩa là ứng dụng web của bạn không cần "gcm_sender_id" nữa.
Trong bài viết này, trước tiên, tôi sẽ mô tả cách chuyển đổi mã máy chủ hiện có để sử dụng Giao thức đẩy web với FCM. Tiếp theo, tôi sẽ hướng dẫn bạn cách triển khai VAPID trong cả mã ứng dụng và mã máy chủ.
FCM hỗ trợ Giao thức đẩy web
Hãy bắt đầu với một chút thông tin bối cảnh. Khi ứng dụng web đăng ký gói thuê bao đẩy, ứng dụng đó sẽ được cung cấp URL của dịch vụ đẩy. Máy chủ của bạn sẽ sử dụng điểm cuối này để gửi dữ liệu đến người dùng thông qua ứng dụng web. Trong Chrome, bạn sẽ được cung cấp một điểm cuối FCM nếu đăng ký người dùng không có VAPID. (Chúng ta sẽ đề cập đến VAPID sau). Trước khi FCM hỗ trợ Giao thức đẩy web, bạn phải trích xuất mã đăng ký FCM từ cuối URL và đặt mã đó vào tiêu đề trước khi tạo yêu cầu API FCM. Ví dụ: điểm cuối FCM của https://android.googleapis.com/gcm/send/ABCD1234
sẽ có mã nhận dạng đăng ký là "ABCD1234".
Giờ đây, khi FCM hỗ trợ Giao thức đẩy web, bạn có thể giữ nguyên điểm cuối và sử dụng URL làm điểm cuối Giao thức đẩy web. (Điều này phù hợp với Firefox và hy vọng là mọi trình duyệt khác trong tương lai.)
Trước khi đi sâu vào VAPID, chúng ta cần đảm bảo mã máy chủ xử lý chính xác điểm cuối FCM. Dưới đây là ví dụ về cách tạo yêu cầu cho dịch vụ đẩy trong Node. Xin lưu ý rằng đối với FCM, chúng ta sẽ thêm khoá API vào tiêu đề yêu cầu. Đối với các điểm cuối dịch vụ đẩy khác, bạn sẽ không cần làm như vậy. Đối với Chrome trước phiên bản 52, Opera Android và Trình duyệt Samsung, bạn vẫn phải thêm "gcm_sender_id" vào tệp manifest.json của ứng dụng web. Khoá API và mã nhận dạng người gửi được dùng để kiểm tra xem máy chủ gửi yêu cầu có thực sự được phép gửi thông báo đến người dùng nhận hay không.
const headers = new Headers();
// 12-hour notification time to live.
headers.append('TTL', 12 * 60 * 60);
// Assuming no data is going to be sent
headers.append('Content-Length', 0);
// Assuming you're not using VAPID (read on), this
// proprietary header is needed
if(subscription.endpoint
.indexOf('https://android.googleapis.com/gcm/send/') === 0) {
headers.append('Authorization', 'GCM_API_KEY');
}
fetch(subscription.endpoint, {
method: 'POST',
headers: headers
})
.then(response => {
if (response.status !== 201) {
throw new Error('Unable to send push message');
}
});
Hãy nhớ rằng đây là thay đổi đối với API của FCM / GCM, vì vậy, bạn không cần cập nhật gói thuê bao, chỉ cần thay đổi mã máy chủ để xác định tiêu đề như minh hoạ ở trên.
Giới thiệu VAPID để xác định máy chủ
VAPID là tên viết tắt mới và thú vị của "Voluntary Application Server Identification" (Mã nhận dạng máy chủ ứng dụng tự nguyện). Về cơ bản, thông số kỹ thuật mới này xác định một quy trình bắt tay giữa máy chủ ứng dụng và dịch vụ đẩy, đồng thời cho phép dịch vụ đẩy xác nhận trang web nào đang gửi thông báo.
Với VAPID, bạn có thể tránh các bước dành riêng cho FCM để gửi thông báo đẩy. Bạn không còn cần dự án Firebase, gcm_sender_id
hoặc tiêu đề Authorization
nữa.
Quy trình này khá đơn giản:
- Máy chủ ứng dụng của bạn tạo một cặp khoá công khai/riêng tư. Khoá công khai được cung cấp cho ứng dụng web của bạn.
- Khi người dùng chọn nhận thông báo đẩy, hãy thêm khoá công khai vào đối tượng tuỳ chọn của lệnh gọi subscribe().
- Khi máy chủ ứng dụng gửi thông báo đẩy, hãy thêm Mã thông báo web JSON đã ký cùng với khoá công khai.
Hãy cùng tìm hiểu chi tiết các bước này.
Tạo cặp khoá công khai/riêng tư
Tôi không giỏi về mã hoá, vì vậy, sau đây là phần liên quan trong thông số kỹ thuật về định dạng khoá công khai/riêng tư VAPID:
Máy chủ ứng dụng PHẢI tạo và duy trì một cặp khoá ký có thể sử dụng với chữ ký số đường cong elip (ECDSA) trên đường cong P-256.
Bạn có thể xem cách thực hiện việc này trong thư viện nút web-push:
function generateVAPIDKeys() {
var curve = crypto.createECDH('prime256v1');
curve.generateKeys();
return {
publicKey: curve.getPublicKey(),
privateKey: curve.getPrivateKey(),
};
}
Đăng ký bằng khoá công khai
Để đăng ký người dùng Chrome nhận thông báo đẩy bằng khoá công khai VAPID, bạn cần truyền khoá công khai dưới dạng Uint8Array bằng cách sử dụng tham số applicationServerKey
của phương thức subscribe().
const publicKey = new Uint8Array([0x4, 0x37, 0x77, 0xfe, …. ]);
serviceWorkerRegistration.pushManager.subscribe(
{
userVisibleOnly: true,
applicationServerKey: publicKey
}
);
Bạn sẽ biết liệu mã này có hoạt động hay không bằng cách kiểm tra điểm cuối trong đối tượng gói thuê bao thu được. Nếu nguồn gốc là fcm.googleapis.com
, thì mã này đang hoạt động.
https://fcm.googleapis.com/fcm/send/ABCD1234
Gửi thông báo đẩy
Để gửi thông báo bằng VAPID, bạn cần tạo một yêu cầu Giao thức đẩy web thông thường với hai tiêu đề HTTP bổ sung: tiêu đề Uỷ quyền và tiêu đề Khoá mã hoá.
Tiêu đề uỷ quyền
Tiêu đề Authorization
là một Mã thông báo web JSON (JWT) đã ký có "WebPush" ở phía trước.
JWT là một cách chia sẻ đối tượng JSON với một bên thứ hai theo cách mà bên gửi có thể ký đối tượng đó và bên nhận có thể xác minh chữ ký là của bên gửi dự kiến. Cấu trúc của JWT là ba chuỗi đã mã hoá, được nối với nhau bằng một dấu chấm.
<JWTHeader>.<Payload>.<Signature>
Tiêu đề JWT
Tiêu đề JWT chứa tên thuật toán dùng để ký và loại mã thông báo. Đối với VAPID, giá trị này phải là:
{
"typ": "JWT",
"alg": "ES256"
}
Sau đó, URL này được mã hoá base64 và tạo thành phần đầu tiên của JWT.
Dung lượng
Trọng tải là một đối tượng JSON khác chứa những thông tin sau:
- Đối tượng ("aud")
- Đây là nguồn gốc của dịch vụ đẩy (KHÔNG PHẢI nguồn gốc của trang web).
Trong JavaScript, bạn có thể làm như sau để lấy đối tượng:
const audience = new URL(subscription.endpoint).origin
- Đây là nguồn gốc của dịch vụ đẩy (KHÔNG PHẢI nguồn gốc của trang web).
Trong JavaScript, bạn có thể làm như sau để lấy đối tượng:
- Thời gian hết hạn ("exp")
- Đây là số giây cho đến khi yêu cầu được coi là đã hết hạn. Thời gian này PHẢI nằm trong vòng 24 giờ kể từ khi yêu cầu được gửi, theo giờ UTC.
- Tiêu đề ("sub")
- Tiêu đề cần phải là một URL hoặc URL
mailto:
. Điều này cung cấp một đầu mối liên hệ trong trường hợp dịch vụ đẩy cần liên hệ với người gửi thông báo.
- Tiêu đề cần phải là một URL hoặc URL
Một tải trọng mẫu có thể có dạng như sau:
{
"aud": "http://push-service.example.com",
"exp": Math.floor((Date.now() / 1000) + (12 * 60 * 60)),
"sub": "mailto: my-email@some-url.com"
}
Đối tượng JSON này được mã hoá URL base64 và tạo thành phần thứ hai của JWT.
Chữ ký
Chữ ký là kết quả của việc nối tiêu đề và tải trọng đã mã hoá bằng dấu chấm, sau đó mã hoá kết quả bằng khoá riêng tư VAPID mà bạn đã tạo trước đó. Kết quả phải được thêm vào tiêu đề bằng dấu chấm.
Tôi sẽ không trình bày mã mẫu cho việc này vì có một số thư viện sẽ lấy tiêu đề và đối tượng JSON tải trọng để tạo chữ ký này cho bạn.
JWT đã ký được dùng làm tiêu đề Uỷ quyền với "WebPush" được thêm vào đầu tiêu đề và sẽ có dạng như sau:
WebPush eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJodHRwczovL2ZjbS5nb29nbGVhcGlzLmNvbSIsImV4cCI6MTQ2NjY2ODU5NCwic3ViIjoibWFpbHRvOnNpbXBsZS1wdXNoLWRlbW9AZ2F1bnRmYWNlLmNvLnVrIn0.Ec0VR8dtf5qb8Fb5Wk91br-evfho9sZT6jBRuQwxVMFyK5S8bhOjk8kuxvilLqTBmDXJM5l3uVrVOQirSsjq0A
Hãy lưu ý một vài điều về vấn đề này. Trước tiên, tiêu đề Uỷ quyền chứa từ "WebPush", theo sau là dấu cách rồi đến JWT. Ngoài ra, hãy lưu ý các dấu chấm phân tách tiêu đề, tải trọng và chữ ký JWT.
Tiêu đề khoá mã hoá
Cũng như tiêu đề Uỷ quyền, bạn phải thêm khoá công khai VAPID vào tiêu đề Crypto-Key
dưới dạng chuỗi được mã hoá URL base64 có p256ecdsa=
ở đầu.
p256ecdsa=BDd3_hVL9fZi9Ybo2UUzA284WG5FZR30_95YeZJsiApwXKpNcF1rRPF3foIiBHXRdJI2Qhumhf6_LFTeZaNndIo
Khi gửi thông báo có dữ liệu đã mã hoá, bạn sẽ sử dụng tiêu đề Crypto-Key
. Vì vậy, để thêm khoá máy chủ ứng dụng, bạn chỉ cần thêm dấu chấm phẩy trước khi thêm nội dung ở trên, kết quả sẽ là:
dh=BGEw2wsHgLwzerjvnMTkbKrFRxdmwJ5S_k7zi7A1coR_sVjHmGrlvzYpAT1n4NPbioFlQkIrTNL8EH4V3ZZ4vJE;
p256ecdsa=BDd3_hVL9fZi9Ybo2UUzA284WG5FZR30_95YeZJsiApwXKpNcF1rRPF3foIiBHXRdJI2Qhumhf6_LFTeZaN
Thực tế về những thay đổi này
Với VAPID, bạn không cần đăng ký tài khoản với GCM để sử dụng tính năng đẩy trong Chrome nữa. Bạn có thể sử dụng cùng một đường dẫn mã để đăng ký người dùng và gửi thông báo cho người dùng trong cả Chrome và Firefox. Cả hai đều tuân thủ các tiêu chuẩn.
Bạn cần lưu ý rằng trong Chrome 51 trở xuống, Opera cho Android và trình duyệt Samsung, bạn vẫn cần xác định gcm_sender_id
trong tệp kê khai ứng dụng web và bạn cần thêm tiêu đề Uỷ quyền vào điểm cuối FCM sẽ được trả về.
VAPID cung cấp một giải pháp thay thế cho các yêu cầu độc quyền này. Nếu bạn triển khai VAPID, thì tính năng này sẽ hoạt động trong tất cả trình duyệt hỗ trợ tính năng đẩy web. Khi có nhiều trình duyệt hơn hỗ trợ VAPID, bạn có thể quyết định thời điểm xoá gcm_sender_id
khỏi tệp kê khai.