Chúng ta đã thấy cách sử dụng một thư viện để kích hoạt thông báo đẩy, nhưng chính xác thì những thư viện này đang làm gì?
Chúng đang thực hiện các yêu cầu mạng trong khi đảm bảo các yêu cầu đó có định dạng phù hợp. Quy cách 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ự xác định bằng khoá máy chủ ứng dụng và cách gửi tải trọng đã mã hoá cùng dữ liệu liên kết.
Đây không phải là một khía cạnh hay 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ì sẽ rất hữu ích nếu bạn biết những thư viện này đang làm gì.
Khoá máy chủ ứng dụng
Khi đăng ký cho một người dùng, chúng ta sẽ truyền vào một applicationServerKey. Khoá này được truyền đến dịch vụ truyền dữ liệu và dùng để kiểm tra xem ứng dụng đã đăng ký người dùng có phải là ứng dụng đang kích hoạt thông báo đẩy hay không.
Khi chúng tôi kích hoạt một thông báo đẩy, sẽ có một bộ tiêu đề mà chúng tôi gửi để 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 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 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ụ truyền tin dưới dạng tiêu đề trong yêu cầu POST.
- Dịch vụ truyền dữ liệu 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ý 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ề quy trình thông tin này. (Lưu ý chú thích ở dưới cùng bên trái để cho 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ột mã thông báo web JSON.
Mã thông báo web JSON
Mã thông báo web JSON (hoặc JWT) là một cách gửi thông báo đến bên thứ ba sao cho người nhận có thể xác thực người đã gửi thông báo đó.
Khi nhận được một thông báo, bên thứ ba cần lấy khoá công khai của người gửi và 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ư phù hợp, do đó phải đến từ 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 đó khi 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ù bạn có thể coi đó là 3 chuỗi được nối 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, tức là có thể đọc công khai.
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 những thông tin sau:
{
"typ": "JWT",
"alg": "ES256"
}
Chuỗi thứ hai là Dữ liệu JWT. Thông tin này cung cấp thông tin về người gửi JWT, người nhận 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 như 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à người mà JWT dành cho. Đối với thông báo đẩy trên web, đối tượng là dịch vụ đẩy, vì vậy, chúng ta đặt đối tượng thành nguồn gốc của dịch vụ đẩy.
Giá trị exp là thời gian hết hạn của JWT, điều này ngăn chặn những kẻ xâm nhập có thể sử dụng lại JWT nếu chúng chặn được JWT. 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 là 12 giờ thay vì 24 giờ để tránh mọi vấn đề về chênh lệch thời gian giữa ứng dụng gửi và dịch vụ truyền tin.
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 một 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ệ trong JWT. (Đây là lý do thư viện web-push cần địa chỉ email).
Giống như JWT Info, JWT Data đượ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 đây là "mã thông báo chưa ký") và ký mã thông báo đó.
Quy 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 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". Bằng cách sử dụng web crypto, 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ụ truyền dữ liệu qua công nghệ đẩ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à 2 chuỗi đầu tiên trong JWT).
JWT đã ký (tức là cả 3 chuỗi được nối bằng dấu chấm) sẽ được gửi đến dịch vụ thông báo đẩy trên web dưới dạng tiêu đề Authorization có WebPush được thêm 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 quy định 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 một chuỗi được mã hoá base64 an toàn cho URL, có thêm p256ecdsa= ở phía trước.
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 một thông báo đẩy để khi ứng dụng web của chúng ta nhận được một thông báo đẩy, ứng dụng đó có thể truy cập vào dữ liệu mà nó 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ụ thông báo đẩy khác là tại sao cần mã hoá tải trọng thông báo đẩy trên web? Với các ứ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 quan trọng 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 thông báo đẩy trên web), nên nhà phát triển không cần quan tâm đến dịch vụ đẩy là gì. Chúng ta có thể đưa ra yêu cầu ở đúng định dạng và dự kiến sẽ nhận được một thông báo đẩy. Nhược điểm của việc này là các nhà phát triển có thể gửi thông báo đến một dịch vụ truyền tin nhắn đẩy không đáng tin cậy. Bằng cách mã hoá tải trọng, dịch vụ truyền tin nhắn đẩ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.
Việc mã hoá tải trọng được xác định trong Thông số mã hoá thông báo.
Trước khi xem xét các bước cụ thể để mã hoá tải trọng của thông báo đẩy, chúng ta nên tìm hiểu một số kỹ thuật sẽ được 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 ông về tính năng mã hoá đẩ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.
Đặc tính hữu ích của các khoá được tạo bằng ECDH là Alice có thể 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ũng có thể làm như vậy, lấy khoá riêng tư của mình và khoá công khai của Alice để độc lập tạo ra cùng một giá trị "X". Điều này khiến 'X' trở thành một khoá 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ể 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 khoá bí mật dùng 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ã, hầu hết các ngôn ngữ / nền tảng đều có cá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 khoá yếu thành khoá mạnh về mặt mật mã. Ví dụ: bạn có thể dùng hàm này để chuyển đổi các bí mật được chia sẻ đã trao đổi Diffie Hellman thành khoá phù hợp để sử 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 đầu vào không đặc biệt an toàn và làm cho đầu vào đó an toàn hơn.
Quy cách 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 quá 256 bit (32 byte).
Trong nút này, 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);
}
Xin 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 bảo mật để chia sẻ khoá công khai và tạo một 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à biến nó thành tài liệu 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 mã hoá dữ liệu đó.
Thông tin đầu vào
Khi muốn gửi thông báo đẩy cho người dùng kèm theo tải trọng, chúng ta cần có 3 dữ liệu đầu vào:
- Chính tải trọng.
- Bí mật
authtừPushSubscription. - Khoá
p256dhtừPushSubscription.
Chúng tôi nhận thấy các giá trị auth và p256dh được truy xuất từ PushSubscription nhưng để nhắc nhanh, đối với một gói thuê bao, chúng tôi cần những 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à một bí mật và không chia sẻ ra bên ngoài ứng dụng của mình.
Khoá p256dh là khoá công khai, đôi khi được gọi là khoá công khai của máy khách. Ở đây, chúng ta 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à dùng khoá này để giải mã tải trọng.
Bạn cần có 3 giá trị này (auth, p256dh và payload) làm dữ liệu đầu vào và kết quả của quy trình mã hoá sẽ là tải trọng được 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 có kích thước 16 byte. Trong NodeJS, chúng ta sẽ làm như sau để tạo một chuỗi ngẫu nhiên:
const salt = crypto.randomBytes(16);
Khoá công khai / Khoá riêng tư
Bạn nên tạo khoá công khai và khoá riêng tư bằng đường cong elip P-256. Chúng ta sẽ thực hiện việc này trong Node như sau:
const localKeysCurve = crypto.createECDH('prime256v1');
localKeysCurve.generateKeys();
const localPublicKey = localKeysCurve.getPublicKey();
const localPrivateKey = localKeysCurve.getPrivateKey();
Chúng ta sẽ gọi những khoá này là "khoá cục bộ". Chúng chỉ được dùng để mã hoá và không liên quan gì đế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 đăng ký làm thông tin đầu vào, cùng với một chuỗi ngẫu nhiên mới tạo và một nhóm 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 tôi (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á ngẫu nhiên giả (PRK).
Khoá giả ngẫu nhiên
Khoá ngẫu nhiên giả (PRK) là sự kết hợp giữa authsecret của gói thuê bao nhận thông báo đẩ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);
Có thể bạn đang thắc mắc chuỗi Content-Encoding: auth\0 dùng để làm gì.
Nói tóm lại, nó không có mục đích rõ ràng, mặc dù các trình duyệt có thể giải mã tin nhắn đến và tìm kiếm nội dung mã hoá dự kiến.
\0 sẽ thêm một byte có giá trị là 0 vào cuối Buffer. Đây là điều mà các trình duyệt giải mã thông báo mong đợi. Các trình duyệt này sẽ mong đợi có nhiều byte như vậy cho quá trình mã hoá nội dung, theo sau là một byte có giá trị 0, theo sau là dữ liệu đã mã hoá.
Khoá ngẫu nhiên giả của chúng tôi chỉ đơn giản là chạy quy trình 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à giúp khoá này mạnh hơn về mặt mật mã).
Ngữ cảnh
"Ngữ cảnh" là một tập hợp các byte được dùng để tính toán 2 giá trị sau này trong trình duyệt mã hoá. Về cơ bản, đây là một mảng gồm các 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 nó trong quá trình 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ị ngăn chặn các cuộc tấn công phát lại vì giá trị này chỉ được dùng một lần.
Khoá mã hoá nội dung (CEK) là khoá 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à bộ đệm ngữ cảnh mà chúng ta vừa tính:
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 salt 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);
Điều này 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 quy trình mã hoá
Giờ đây, 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ậ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à một vectơ khởi tạo.
Trong Node, bạn có thể 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 phần đệm mà chúng ta muốn thêm vào đầu 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 "các 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 mọi khoảng đệm bổ sung.
Ví dụ: nếu không thêm khoảng đệm, bạn sẽ có 2 byte có 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 đệm, 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 ta 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!
Bạn chỉ cần xác định cách gửi tải trọng này đến dịch vụ truyền tin.
Tiêu đề và nội dung của tải trọng đã mã hoá
Để gửi tải trọng đã mã hoá này đến dịch vụ truyền tin, chúng ta cần xác định một số 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.
Giá trị salt 16 byte phải được mã hoá an toàn bằng 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 tôi nhận thấy tiêu đề Crypto-Key được dùng trong phần "Khoá máy chủ ứng dụng" để chứa khoá công khai của máy chủ ứng dụng.
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 đề về loại nội dung, độ dài và mã hoá
Tiêu đề Content-Length là số byte trong tải trọng được mã hoá. Tiêu đề "Content-Type" và "Content-Encoding" là các giá trị cố định.
Điều này được minh hoạ bên dưới.
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á làm 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 cần thêm phần đầu trang?
Chúng ta đã đề cập đến 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ụ truyền dữ liệu đẩy) và chúng ta cũng đã đề cập đến các tiêu đề dùng để gửi tải trọng được mã hoá.
Ngoài ra, còn có các tiêu đề khác mà dịch vụ truyền tin dùng để thay đổi hành vi của các thư đã gửi. Một số tiêu đề trong số này là bắt buộc, còn các 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 dịch vụ truyền tin 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ụ truyền tin sẽ cố gắng gửi thông báo ngay lập tức, nhưng nếu không thể truy cập vào thiết bị, thông báo của bạn sẽ bị loại bỏ ngay khỏi hàng đợi của dịch vụ truyền tin.
Về mặt kỹ thuật, dịch vụ đẩy có thể giảm TTL của một 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 của một dịch vụ truyền dữ liệu.
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 hữu ích trong trường hợp nhiều thông báo được gửi khi thiết bị đang ở chế độ ngoại tuyến và bạn 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 biết mức độ quan trọng của một thông báo đối với người dùng đối với dịch vụ đẩy. Dịch vụ truyền tin có thể dùng thông tin này để giúp duy trì thời lượng pin của thiết bị người dùng bằng cách chỉ kích hoạt cho các thông báo quan trọng khi pin yếu.
Giá trị tiêu đề được xác định như minh hoạ bên dưới. Giá trị mặc định là normal.
Urgency: [very-low | low | normal | high]
Mọi thứ đều ở cùng một nơi
Nếu có thêm câu hỏi về cách thức 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 web-push-libs org.
Sau khi có tải trọng đã mã hoá và các tiê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ụ truyền dữ liệu
Sau khi đưa ra yêu cầu cho một dịch vụ truyền tin, 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. Hệ thống đã nhận được và chấp nhận yêu cầu gửi thông báo đẩy. |
| 429 | Quá nhiều yêu cầu. 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ụ truyền dữ liệu. Dịch vụ truyền dữ liệu qua push phải có tiêu đề "Retry-After" để cho biết khoảng thời gian trước khi có thể đưa ra một yêu cầu khác. |
| 400 | Yêu cầu không hợp lệ. Lỗi 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. Đây là dấu hiệu cho thấy gói thuê bao đã hết hạn và không thể sử dụng. Trong trường hợp này, bạn nên xoá `PushSubscription` và đợi cho đến khi ứng dụng khách đăng ký lại người dùng. |
| 410 | Không còn. 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 tạo điều này bằng cách gọi `unsubscribe()` trên một `PushSubscription`. |
| 413 | Kích thước tải trọng quá lớn. Tải trọng kích thước tối thiểu mà dịch vụ truyền tin nhắn đẩy phải hỗ trợ là 4096 byte (hoặc 4 KB). |
Bạn cũng có thể đọc tiêu chuẩn Web Push (RFC8030) để biết thêm thông tin về mã trạng thái HTTP.
Nơi cần đến tiếp theo
- Tổng quan về thông báo đẩy trên web
- Cách hoạt động của tính năng 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 thường gặp
- 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