Chúng ta đã thấy cách sử dụng thư viện để kích hoạt thông báo đẩy, nhưng chính xác thì các thư viện này đang làm gì?
Họ đang đưa ra các yêu cầu mạng, đồng thời đảm bảo rằng các yêu cầu đó có định dạng phù hợp. Thông số kỹ thuật xác định yêu cầu mạng này là Giao thức thông báo đẩy trên web.
Phần này trình bày cách máy chủ có thể tự nhận dạng bằng khoá máy chủ ứng dụng, cũng như cách gửi tải trọng đã mã hoá và dữ liệu liên quan.
Đây không phải là một khía cạnh dễ chịu của thông báo đẩy trên web và tôi không phải là chuyên gia về mã hoá, nhưng hãy xem xét từng phần vì việc biết các thư viện này đang làm gì là rất hữu ích.
Khoá máy chủ ứng dụng
Khi đăng ký cho người dùng, chúng ta sẽ truyền vào một applicationServerKey. Khoá này được chuyển đến dịch vụ đẩy và dùng để kiểm tra xem ứng dụng mà người dùng đã đăng ký cũng là ứng dụng kích hoạt thông báo đẩy.
Khi kích hoạt một thông báo đẩy, chúng tôi sẽ gửi một tập hợp tiêu đề cho phép dịch vụ đẩy xác thực ứng dụng. (Điều này được xác định theo quy cách VAPID.)
Tất cả những điều này thực sự có nghĩa là gì và chính xác thì điều gì sẽ xảy ra? Sau đây là các bước thực hiện xác thực máy chủ ứng dụng:
- Máy chủ ứng dụng ký một số thông tin JSON bằng khoá ứng dụng riêng tư.
- Thông tin đã ký này được gửi đến dịch vụ đẩy dưới dạng tiêu đề trong yêu cầu POST.
- Dịch vụ đẩy sử dụng khoá công khai đã lưu trữ mà dịch vụ này nhận được từ
pushManager.subscribe()để kiểm tra xem thông tin nhận được có được ký bằng khoá riêng tư liên quan đến khoá công khai hay không. Lưu ý: Khoá công khai làapplicationServerKeyđược truyền vào lệnh gọi đăng ký. - Nếu thông tin đã ký là hợp lệ, dịch vụ đẩy sẽ gửi thông báo đẩy đến người dùng.
Dưới đây là ví dụ về luồng thông tin này. (Lưu ý chú giải ở dưới cùng bên trái để biết khoá công khai và khoá riêng tư.)
"Thông tin đã ký" được thêm vào tiêu đề trong yêu cầu là Mã thông báo web JSON.
Mã thông báo web JSON
Mã thông báo web JSON (hoặc viết tắt là JWT) là một cách gửi thông báo đến bên thứ ba để người nhận có thể xác thực người gửi.
Khi nhận được thông báo, bên thứ ba cần lấy khoá công khai của người gửi và sử dụng khoá đó để xác thực chữ ký của JWT. Nếu chữ ký hợp lệ, thì JWT phải được ký bằng khoá riêng tư khớp với chữ ký đó, vì vậy, JWT phải là của người gửi dự kiến.
Có rất nhiều thư viện trên jwt.io/ có thể thực hiện việc ký cho bạn và bạn nên làm việc đó nếu có thể. Để hoàn tất, hãy xem cách tạo JWT đã ký theo cách thủ công.
Thông báo đẩy trên web và JWT đã ký
JWT đã ký chỉ là một chuỗi, mặc dù có thể coi là ba chuỗi được nối với nhau bằng dấu chấm.
Chuỗi thứ nhất và thứ hai (Thông tin JWT và dữ liệu JWT) là các đoạn JSON đã được mã hoá base64, nghĩa là mọi người đều có thể đọc được.
Chuỗi đầu tiên là thông tin về chính JWT, cho biết thuật toán nào đã được dùng để tạo chữ ký.
Thông tin JWT cho thông báo đẩy trên web phải chứa các thông tin sau:
{
"typ": "JWT",
"alg": "ES256"
}
Chuỗi thứ hai là Dữ liệu JWT. Phần này cung cấp thông tin về người gửi JWT, đối tượng nhận JWT và thời hạn hiệu lực của JWT.
Đối với thông báo đẩy trên web, dữ liệu sẽ có định dạng sau:
{
"aud": "https://some-push-service.org",
"exp": "1469618703",
"sub": "mailto:example@web-push-book.org"
}
Giá trị aud là "đối tượng", tức là đối tượng mà JWT hướng đến. Đối với thông báo đẩy trên web, đối tượng là dịch vụ thông báo đẩy, vì vậy, chúng ta đặt đối tượng này thành nguồn gốc của dịch vụ thông báo đẩy.
Giá trị exp là thời gian hết hạn của JWT, giúp ngăn chặn những kẻ rình mò có thể sử dụng lại JWT nếu chúng chặn được. Thời gian hết hạn là dấu thời gian tính bằng giây và không được quá 24 giờ.
Trong Node.js, thời gian hết hạn được đặt bằng cách sử dụng:
Math.floor(Date.now() / 1000) + 12 * 60 * 60;
Thời gian này là 12 giờ thay vì 24 giờ để tránh mọi vấn đề về chênh lệch đồng hồ giữa ứng dụng gửi và dịch vụ đẩy.
Cuối cùng, giá trị sub phải là một URL hoặc địa chỉ email mailto.
Điều này là để nếu dịch vụ đẩy cần liên hệ với người gửi, thì dịch vụ đó có thể tìm thấy thông tin liên hệ từ JWT. (Đây là lý do thư viện web-push cần có địa chỉ email).
Giống như Thông tin JWT, Dữ liệu JWT được mã hoá dưới dạng chuỗi base64 an toàn cho URL.
Chuỗi thứ ba, chữ ký, là kết quả của việc lấy hai chuỗi đầu tiên (Thông tin JWT và Dữ liệu JWT), nối chúng bằng một ký tự dấu chấm (chúng ta sẽ gọi là "mã thông báo chưa ký") và ký mã thông báo đó.
Quá trình ký yêu cầu mã hoá "mã thông báo chưa ký" bằng ES256. Theo thông số kỹ thuật của JWT, ES256 là viết tắt của "ECDSA sử dụng đường cong P-256 và thuật toán băm SHA-256". Khi sử dụng mật mã web, bạn có thể tạo chữ ký như sau:
// Utility function for UTF-8 encoding a string to an ArrayBuffer.
const utf8Encoder = new TextEncoder('utf-8');
// The unsigned token is the concatenation of the URL-safe base64 encoded
// header and body.
const unsignedToken = .....;
// Sign the |unsignedToken| using ES256 (SHA-256 over ECDSA).
const key = {
kty: 'EC',
crv: 'P-256',
x: window.uint8ArrayToBase64Url(
applicationServerKeys.publicKey.subarray(1, 33)),
y: window.uint8ArrayToBase64Url(
applicationServerKeys.publicKey.subarray(33, 65)),
d: window.uint8ArrayToBase64Url(applicationServerKeys.privateKey),
};
// Sign the |unsignedToken| with the server's private key to generate
// the signature.
return crypto.subtle.importKey('jwk', key, {
name: 'ECDSA', namedCurve: 'P-256',
}, true, ['sign'])
.then((key) => {
return crypto.subtle.sign({
name: 'ECDSA',
hash: {
name: 'SHA-256',
},
}, key, utf8Encoder.encode(unsignedToken));
})
.then((signature) => {
console.log('Signature: ', signature);
});
Dịch vụ đẩy có thể xác thực JWT bằng khoá máy chủ ứng dụng công khai để giải mã chữ ký và đảm bảo chuỗi đã giải mã giống với "mã thông báo chưa ký" (tức là hai chuỗi đầu tiên trong JWT).
JWT đã ký (tức là cả ba chuỗi được nối bằng dấu chấm) được gửi đến dịch vụ thông báo đẩy trên web dưới dạng tiêu đề Authorization có thêm WebPush vào trước, như sau:
Authorization: 'WebPush [JWT Info].[JWT Data].[Signature]';
Giao thức thông báo đẩy trên web cũng nêu rõ rằng khoá máy chủ ứng dụng công khai phải được gửi trong tiêu đề Crypto-Key dưới dạng chuỗi được mã hoá base64 an toàn cho URL và có thêm p256ecdsa= ở đầu.
Crypto-Key: p256ecdsa=[URL Safe Base64 Public Application Server Key]
Mã hoá tải trọng
Tiếp theo, hãy xem cách chúng ta có thể gửi tải trọng bằng thông báo đẩy để khi ứng dụng web của chúng ta nhận được thông báo đẩy, ứng dụng đó có thể truy cập vào dữ liệu nhận được.
Một câu hỏi thường gặp của những người đã sử dụng các dịch vụ đẩy khác là tại sao tải trọng đẩy web cần được mã hoá? Với ứng dụng gốc, thông báo đẩy có thể gửi dữ liệu dưới dạng văn bản thuần tuý.
Một phần vẻ đẹp của thông báo đẩy trên web là vì tất cả dịch vụ đẩy đều sử dụng cùng một API (giao thức đẩy trên web), nên nhà phát triển không cần quan tâm đến dịch vụ đẩy là ai. Chúng ta có thể đưa ra yêu cầu theo đúng định dạng và mong đợi một thông báo đẩy được gửi đi. Nhược điểm của việc này là nhà phát triển có thể gửi thông báo đến một dịch vụ đẩy không đáng tin cậy. Bằng cách mã hoá tải trọng, dịch vụ đẩy không thể đọc dữ liệu được gửi. Chỉ trình duyệt mới có thể giải mã thông tin. Điều này giúp bảo vệ dữ liệu của người dùng.
Quá trình mã hoá tải trọng được xác định trong Thông số kỹ thuật mã hoá thông báo.
Trước khi xem xét các bước cụ thể để mã hoá tải trọng thông báo đẩy, chúng ta nên tìm hiểu một số kỹ thuật sẽ được sử dụng trong quá trình mã hoá. (Xin chân thành cảm ơn Mat Scales vì bài viết xuất sắc của anh về tính năng mã hoá thông báo đẩy.)
ECDH và HKDF
Cả ECDH và HKDF đều được dùng trong suốt quá trình mã hoá và mang lại lợi ích cho mục đích mã hoá thông tin.
ECDH: Trao đổi khoá Diffie-Hellman đường cong elip
Hãy tưởng tượng bạn có hai người muốn chia sẻ thông tin, Alice và Bob. Cả Alice và Bob đều có khoá công khai và khoá riêng tư của riêng mình. Alice và Bob chia sẻ khoá công khai của họ với nhau.
Thuộc tính hữu ích của các khoá được tạo bằng ECDH là Alice có thể sử dụng khoá riêng tư của mình và khoá công khai của Bob để tạo giá trị bí mật "X". Bob có thể làm tương tự, lấy khoá riêng tư của mình và khoá công khai của Alice để tạo ra cùng một giá trị "X" một cách độc lập. Điều này khiến "X" trở thành một bí mật dùng chung và Alice và Bob chỉ phải chia sẻ khoá công khai của họ. Giờ đây, Bob và Alice có thể sử dụng "X" để mã hoá và giải mã thư giữa họ.
Theo những gì tôi biết, ECDH xác định các thuộc tính của đường cong cho phép "tính năng" này tạo ra một bí mật chung "X".
Đây là nội dung giải thích tổng quan về ECDH. Nếu muốn tìm hiểu thêm, bạn nên xem video tổng quan chi tiết hơn về ECDH.
Về mặt mã; hầu hết các ngôn ngữ/nền tảng đều có thư viện để giúp bạn dễ dàng tạo các khoá này.
Trong nút, chúng ta sẽ làm như sau:
const keyCurve = crypto.createECDH('prime256v1');
keyCurve.generateKeys();
const publicKey = keyCurve.getPublicKey();
const privateKey = keyCurve.getPrivateKey();
HKDF: Hàm dẫn xuất khoá dựa trên HMAC
Wikipedia có một nội dung mô tả ngắn gọn về HKDF:
HKDF là một hàm dẫn xuất khoá dựa trên HMAC, có chức năng chuyển đổi mọi nội dung khoá yếu thành nội dung khoá mạnh về mặt mã hoá. Ví dụ: bạn có thể dùng hàm này để chuyển đổi khoá bí mật dùng chung được trao đổi qua Diffie Hellman thành khoá phù hợp để dùng trong quá trình mã hoá, kiểm tra tính toàn vẹn hoặc xác thực.
Về cơ bản, HKDF sẽ lấy dữ liệu đầu vào không an toàn và làm cho dữ liệu đó an toàn hơn.
Thông số kỹ thuật xác định phương thức mã hoá này yêu cầu sử dụng SHA-256 làm thuật toán băm và các khoá kết quả cho HKDF trong thông báo đẩy trên web không được dài hơn 256 bit (32 byte).
Trong Node, bạn có thể triển khai như sau:
// Simplified HKDF, returning keys up to 32 bytes long
function hkdf(salt, ikm, info, length) {
// Extract
const keyHmac = crypto.createHmac('sha256', salt);
keyHmac.update(ikm);
const key = keyHmac.digest();
// Expand
const infoHmac = crypto.createHmac('sha256', key);
infoHmac.update(info);
// A one byte long buffer containing only 0x01
const ONE_BUFFER = new Buffer(1).fill(1);
infoHmac.update(ONE_BUFFER);
return infoHmac.digest().slice(0, length);
}
Cảm ơn bài viết của Mat Scale về mã ví dụ này.
Điều này bao gồm ECDH và HKDF.
ECDH là một cách an toàn để chia sẻ khoá công khai và tạo khoá bí mật dùng chung. HKDF là một cách để lấy tài liệu không an toàn và làm cho tài liệu đó trở nên an toàn.
Khoá này sẽ được dùng trong quá trình mã hoá tải trọng của chúng tôi. Tiếp theo, hãy xem xét những gì chúng ta lấy làm dữ liệu đầu vào và cách dữ liệu đó được mã hoá.
Thông tin đầu vào
Khi muốn gửi thông báo đẩy cho người dùng có tải trọng, chúng ta cần có 3 dữ liệu đầu vào:
- Chính tải trọng.
- Mật khẩu
authtừPushSubscription. - Khoá
p256dhtừPushSubscription.
Chúng ta đã thấy các giá trị auth và p256dh được truy xuất từ PushSubscription, nhưng để nhắc lại nhanh, với một gói thuê bao, chúng ta cần các giá trị sau:
subscription.toJSON().keys.auth;
subscription.toJSON().keys.p256dh;
subscription.getKey('auth');
subscription.getKey('p256dh');
Bạn nên coi giá trị auth là thông tin bí mật và không chia sẻ ra bên ngoài ứng dụng.
Khoá p256dh là khoá công khai, đôi khi được gọi là khoá công khai của ứng dụng. Tại đây,
chúng tôi sẽ gọi p256dh là khoá công khai của gói thuê bao. Khoá công khai của gói thuê bao do trình duyệt tạo. Trình duyệt sẽ giữ bí mật khoá riêng tư và sử dụng khoá này để giải mã tải trọng.
Ba giá trị này (auth, p256dh và payload) là cần thiết làm dữ liệu đầu vào và kết quả của quá trình mã hoá sẽ là tải trọng đã mã hoá, giá trị dữ liệu ngẫu nhiên và khoá công khai chỉ dùng để mã hoá dữ liệu.
Salt
Dữ liệu ngẫu nhiên này phải là 16 byte dữ liệu ngẫu nhiên. Trong NodeJS, chúng ta sẽ làm như sau để tạo muối:
const salt = crypto.randomBytes(16);
Khoá công khai/Khoá riêng tư
Khoá công khai và khoá riêng tư phải được tạo bằng đường cong elip P-256, chúng ta sẽ thực hiện trong Node như sau:
const localKeysCurve = crypto.createECDH('prime256v1');
localKeysCurve.generateKeys();
const localPublicKey = localKeysCurve.getPublicKey();
const localPrivateKey = localKeysCurve.getPrivateKey();
Chúng tôi sẽ gọi các khoá này là "khoá cục bộ". Các khoá này chỉ được dùng để mã hoá và không liên quan đến khoá máy chủ ứng dụng.
Với tải trọng, khoá bí mật xác thực và khoá công khai của gói thuê bao làm dữ liệu đầu vào, cùng với một muối mới được tạo và một bộ khoá cục bộ, chúng ta đã sẵn sàng thực hiện một số hoạt động mã hoá.
Khoá bí mật dùng chung
Bước đầu tiên là tạo một khoá bí mật dùng chung bằng khoá công khai của gói thuê bao và khoá riêng tư mới của chúng ta (bạn có nhớ phần giải thích về ECDH với Alice và Bob không? Chỉ đơn giản vậy thôi).
const sharedSecret = localKeysCurve.computeSecret(
subscription.keys.p256dh,
'base64',
);
Giá trị này được dùng trong bước tiếp theo để tính Khoá giả ngẫu nhiên (PRK).
Khoá giả ngẫu nhiên
Khoá giả ngẫu nhiên (PRK) là sự kết hợp giữa khoá bí mật xác thực của gói thuê bao đẩy và khoá bí mật dùng chung mà chúng ta vừa tạo.
const authEncBuff = new Buffer('Content-Encoding: auth\0', 'utf8');
const prk = hkdf(subscription.keys.auth, sharedSecret, authEncBuff, 32);
Bạn có thể thắc mắc chuỗi Content-Encoding: auth\0 dùng để làm gì.
Nói tóm lại, tiêu đề này không có mục đích rõ ràng, mặc dù trình duyệt có thể giải mã một tin nhắn đến và tìm phương thức mã hoá nội dung dự kiến.
\0 thêm một byte có giá trị là 0 vào cuối Bộ đệm. Đây là điều mà các trình duyệt giải mã thông báo mong đợi, tức là sẽ có nhiều byte cho mã hoá nội dung, theo sau là một byte có giá trị 0, theo sau là dữ liệu đã mã hoá.
Khoá giả ngẫu nhiên của chúng tôi chỉ đơn giản là chạy thông tin xác thực, khoá bí mật dùng chung và một phần thông tin mã hoá thông qua HKDF (tức là tăng cường khả năng mã hoá).
Ngữ cảnh
"Bối cảnh" là một tập hợp các byte được dùng để tính toán hai giá trị sau này trong trình duyệt mã hoá. Về cơ bản, đây là một mảng byte chứa khoá công khai của gói thuê bao và khoá công khai cục bộ.
const keyLabel = new Buffer('P-256\0', 'utf8');
// Convert subscription public key into a buffer.
const subscriptionPubKey = new Buffer(subscription.keys.p256dh, 'base64');
const subscriptionPubKeyLength = new Uint8Array(2);
subscriptionPubKeyLength[0] = 0;
subscriptionPubKeyLength[1] = subscriptionPubKey.length;
const localPublicKeyLength = new Uint8Array(2);
subscriptionPubKeyLength[0] = 0;
subscriptionPubKeyLength[1] = localPublicKey.length;
const contextBuffer = Buffer.concat([
keyLabel,
subscriptionPubKeyLength.buffer,
subscriptionPubKey,
localPublicKeyLength.buffer,
localPublicKey,
]);
Vùng đệm ngữ cảnh cuối cùng là một nhãn, số byte trong khoá công khai của gói thuê bao, theo sau là chính khoá đó, sau đó là số byte khoá công khai cục bộ, theo sau là chính khoá đó.
Với giá trị bối cảnh này, chúng ta có thể sử dụng giá trị này để tạo số chỉ dùng một lần và khoá mã hoá nội dung (CEK).
Khoá mã hoá nội dung và số chỉ dùng một lần
Số chỉ dùng một lần là một giá trị giúp ngăn chặn các cuộc tấn công lặp lại vì giá trị này chỉ được dùng một lần.
Khoá mã hoá nội dung (CEK) là khoá cuối cùng sẽ được dùng để mã hoá tải trọng của chúng ta.
Trước tiên, chúng ta cần tạo các byte dữ liệu cho số chỉ dùng một lần và CEK, đây chỉ đơn giản là một chuỗi mã hoá nội dung, theo sau là vùng đệm ngữ cảnh mà chúng ta vừa tính toán:
const nonceEncBuffer = new Buffer('Content-Encoding: nonce\0', 'utf8');
const nonceInfo = Buffer.concat([nonceEncBuffer, contextBuffer]);
const cekEncBuffer = new Buffer('Content-Encoding: aesgcm\0');
const cekInfo = Buffer.concat([cekEncBuffer, contextBuffer]);
Thông tin này được chạy thông qua HKDF kết hợp muối và PRK với nonceInfo và cekInfo:
// The nonce should be 12 bytes long
const nonce = hkdf(salt, prk, nonceInfo, 12);
// The CEK should be 16 bytes long
const contentEncryptionKey = hkdf(salt, prk, cekInfo, 16);
Thao tác này sẽ cung cấp cho chúng ta số chỉ dùng một lần và khoá mã hoá nội dung.
Thực hiện mã hoá
Bây giờ, khi đã có khoá mã hoá nội dung, chúng ta có thể mã hoá tải trọng.
Chúng tôi tạo mật mã AES128 bằng cách sử dụng khoá mã hoá nội dung làm khoá và số chỉ dùng một lần là vectơ khởi tạo.
Trong Node, việc này được thực hiện như sau:
const cipher = crypto.createCipheriv(
'id-aes128-GCM',
contentEncryptionKey,
nonce,
);
Trước khi mã hoá tải trọng, chúng ta cần xác định lượng khoảng đệm muốn thêm vào phía trước tải trọng. Lý do chúng ta muốn thêm khoảng đệm là để ngăn chặn nguy cơ người nghe lén có thể xác định "loại" thông báo dựa trên kích thước tải trọng.
Bạn phải thêm 2 byte khoảng đệm để cho biết độ dài của khoảng đệm bổ sung.
Ví dụ: nếu không thêm khoảng đệm, bạn sẽ có 2 byte với giá trị 0, tức là không có khoảng đệm. Sau 2 byte này, bạn sẽ đọc tải trọng. Nếu bạn thêm 5 byte khoảng đệm, thì 2 byte đầu tiên sẽ có giá trị là 5, vì vậy, người dùng sẽ đọc thêm 5 byte rồi bắt đầu đọc tải trọng.
const padding = new Buffer(2 + paddingLength);
// The buffer must be only zeros, except the length
padding.fill(0);
padding.writeUInt16BE(paddingLength, 0);
Sau đó, chúng tôi chạy khoảng đệm và tải trọng thông qua mật mã này.
const result = cipher.update(Buffer.concat(padding, payload));
cipher.final();
// Append the auth tag to the result -
// https://nodejs.org/api/crypto.html#crypto_cipher_getauthtag
const encryptedPayload = Buffer.concat([result, cipher.getAuthTag()]);
Giờ đây, chúng ta đã có tải trọng được mã hoá. Thật tuyệt vời!
Việc còn lại là xác định cách gửi tải trọng này đến dịch vụ đẩy.
Phần đầu và phần thân của tải trọng đã mã hoá
Để gửi tải trọng đã mã hoá này đến dịch vụ đẩy, chúng ta cần xác định một vài tiêu đề khác nhau trong yêu cầu POST.
Tiêu đề mã hoá
Tiêu đề "Mã hoá" phải chứa salt dùng để mã hoá tải trọng.
Salt 16 byte phải được mã hoá an toàn theo URL base64 và được thêm vào tiêu đề Mã hoá, như sau:
Encryption: salt=[URL Safe Base64 Encoded Salt]
Tiêu đề Crypto-Key
Chúng ta đã thấy tiêu đề Crypto-Key được dùng trong phần "Khoá máy chủ ứng dụng" để chứa khoá máy chủ ứng dụng công khai.
Tiêu đề này cũng được dùng để chia sẻ khoá công khai cục bộ dùng để mã hoá tải trọng.
Tiêu đề kết quả sẽ có dạng như sau:
Crypto-Key: dh=[URL Safe Base64 Encoded Local Public Key String]; p256ecdsa=[URL Safe Base64 Encoded Public Application Server Key]
Tiêu đề loại nội dung, độ dài và mã hoá
Tiêu đề Content-Length là số byte trong tải trọng đã mã hoá. Tiêu đề "Content-Type" và "Content-Encoding" là các giá trị cố định.
Điều này được minh hoạ dưới đây.
Content-Length: [Number of Bytes in Encrypted Payload]
Content-Type: 'application/octet-stream'
Content-Encoding: 'aesgcm'
Sau khi đặt các tiêu đề này, chúng ta cần gửi tải trọng đã mã hoá dưới dạng nội dung của yêu cầu. Lưu ý rằng Content-Type được đặt thành application/octet-stream. Điều này là do tải trọng đã mã hoá phải được gửi dưới dạng một luồng byte.
Trong NodeJS, chúng ta sẽ làm như sau:
const pushRequest = https.request(httpsOptions, function(pushResponse) {
pushRequest.write(encryptedPayload);
pushRequest.end();
Bạn muốn thêm tiêu đề?
Chúng ta đã tìm hiểu về các tiêu đề dùng cho JWT/Khoá máy chủ ứng dụng (tức là cách xác định ứng dụng bằng dịch vụ đẩy) và chúng ta đã tìm hiểu về các tiêu đề dùng để gửi tải trọng đã mã hoá.
Có thêm các tiêu đề mà dịch vụ đẩy sử dụng để thay đổi hành vi của thông báo đã gửi. Một số tiêu đề là bắt buộc, trong khi một số tiêu đề khác là không bắt buộc.
Tiêu đề TTL
Bắt buộc
TTL (hoặc thời gian tồn tại) là một số nguyên chỉ định số giây bạn muốn thông báo đẩy tồn tại trên dịch vụ đẩy trước khi được gửi. Khi TTL hết hạn, thông báo sẽ bị xoá khỏi hàng đợi của dịch vụ thông báo đẩy và sẽ không được gửi.
TTL: [Time to live in seconds]
Nếu bạn đặt TTL bằng 0, dịch vụ đẩy sẽ cố gắng gửi thông báo ngay lập tức, nhưng nếu không thể kết nối với thiết bị, thông báo của bạn sẽ ngay lập tức bị loại bỏ khỏi hàng đợi của dịch vụ đẩy.
Về mặt kỹ thuật, dịch vụ đẩy có thể giảm TTL của thông báo đẩy nếu muốn. Bạn có thể biết điều này có xảy ra hay không bằng cách kiểm tra tiêu đề TTL trong phản hồi từ dịch vụ đẩy.
Chủ đề
Không bắt buộc
Chủ đề là các chuỗi có thể dùng để thay thế một thông báo đang chờ xử lý bằng một thông báo mới nếu chúng có tên chủ đề trùng khớp.
Điều này rất hữu ích trong trường hợp nhiều thông báo được gửi khi thiết bị không có kết nối mạng và bạn thực sự chỉ muốn người dùng nhìn thấy thông báo mới nhất khi thiết bị được bật.
Khẩn cấp
Không bắt buộc
Mức độ khẩn cấp cho dịch vụ đẩy biết mức độ quan trọng của thông báo đối với người dùng. Dịch vụ đẩy có thể sử dụng thông tin này để giúp tiết kiệm pin cho thiết bị của người dùng bằng cách chỉ đánh thức thiết bị khi có thông báo quan trọng và pin yếu.
Giá trị tiêu đề được xác định như dưới đây. Giá trị mặc định là normal.
Urgency: [very-low | low | normal | high]
Mọi thứ cùng nhau
Nếu có thêm câu hỏi về cách hoạt động của tất cả những điều này, bạn luôn có thể xem cách các thư viện kích hoạt thông báo đẩy trên tổ chức web-push-libs.
Sau khi có tải trọng đã mã hoá và các tiêu đề nêu trên, bạn chỉ cần gửi một yêu cầu POST đến endpoint trong PushSubscription.
Vậy chúng ta sẽ làm gì với phản hồi cho yêu cầu POST này?
Phản hồi từ dịch vụ đẩy
Sau khi gửi yêu cầu đến dịch vụ đẩy, bạn cần kiểm tra mã trạng thái của phản hồi vì mã này sẽ cho bạn biết yêu cầu có thành công hay không.
| Mã trạng thái | Mô tả |
|---|---|
| 201 | Đã tạo. Yêu cầu gửi thông báo đẩy đã được nhận và chấp nhận. |
| 429 | Quá nhiều yêu cầu. Điều này có nghĩa là máy chủ ứng dụng của bạn đã đạt đến giới hạn tốc độ với một dịch vụ đẩy. Dịch vụ đẩy phải bao gồm tiêu đề "Retry-After" để cho biết thời gian trước khi có thể thực hiện một yêu cầu khác. |
| 400 | Yêu cầu không hợp lệ. Điều này thường có nghĩa là một trong các tiêu đề của bạn không hợp lệ hoặc có định dạng không chính xác. |
| 404 | Không tìm thấy. Điều này cho biết gói thuê bao đã hết hạn và không thể sử dụng được. Trong trường hợp này, bạn nên xoá `PushSubscription` và đợi ứng dụng đăng ký lại cho người dùng. |
| 410 | Không tồn tại. Gói thuê bao không còn hợp lệ và cần được xoá khỏi máy chủ ứng dụng. Bạn có thể tái hiện điều này bằng cách gọi `unsubscribe()` trên `PushSubscription`. |
| 413 | Kích thước tải trọng quá lớn. Kích thước tải trọng tối thiểu mà dịch vụ đẩy phải hỗ trợ là 4096 byte (hoặc 4kb). |
Bạn cũng có thể đọc tiêu chuẩn Thông báo đẩy trên web (RFC8030) để biết thêm thông tin về mã trạng thái HTTP.
Điểm đến tiếp theo
- Tổng quan về thông báo đẩy trên web
- Cách hoạt động của thông báo đẩy
- Đăng ký cho người dùng
- Trải nghiệm người dùng về quyền
- Gửi thông báo bằng thư viện thông báo đẩy trên web
- Giao thức thông báo đẩy trên web
- Xử lý sự kiện đẩy
- Hiển thị thông báo
- Hành vi thông báo
- Các mẫu thông báo phổ biến
- Câu hỏi thường gặp về thông báo đẩy
- Các vấn đề thường gặp và cách báo cáo lỗi