Giao thức đẩy web

Chúng ta đã biết cách dùng thư viện để kích hoạt thông báo đẩy, nhưng điều gì chính xác thì những thư viện này đang hoạt động như thế nào?

Ồ, họ tạo yêu cầu mạng trong khi vẫn đảm bảo rằng những yêu cầu như vậy theo đúng định dạng. Thông số xác định yêu cầu mạng này là Web Push Protocol (Giao thức đẩy web).

Sơ đồ gửi thông báo đẩy từ máy chủ đến thông báo đẩy
dịch vụ

Phần này trình bày cách máy chủ có thể tự xác định bằng ứng dụng khoá máy chủ và 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 vấn đề quan trọng trong lĩnh vực web và tôi không có chuyên môn về mã hoá, nhưng hãy cùng xem qua từng phần vì việc biết các thư viện này đang làm gì sẽ rất hữu ích.

Khoá máy chủ ứng dụng

Khi đăng ký một người dùng, chúng ta sẽ truyền vào một applicationServerKey. Chìa khoá này là được chuyển đến dịch vụ đẩy và dùng để kiểm tra xem ứng dụng đã đăng ký người dùng cũng là ứng dụng kích hoạt thông báo đẩy.

Khi chúng tôi kích hoạt một thông báo đẩy, có một tập hợp các 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 định nghĩa theo quy cách VAPID.)

Tất cả những điều này có ý nghĩa gì và chính xác thì xảy ra điều gì? Đây là các bước được thực hiện để xác thực máy chủ ứng dụng:

  1. Máy chủ ứng dụng ký một số thông tin JSON bằng khoá ứng dụng riêng tư của máy chủ đó.
  2. 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.
  3. Dịch vụ đẩy sử dụng khoá công khai được lưu trữ nhận được từ pushManager.subscribe() để kiểm tra để kiểm tra xem thông tin đã nhận có được ký bằng khoá riêng tư liên quan đến khoá công khai. Lưu ý: Khoá công khai là applicationServerKey được chuyển vào cuộc gọi đăng ký.
  4. Nếu thông tin đã ký là hợp lệ, dịch vụ thông báo đẩy sẽ gửi thông báo đẩy thông báo cho người dùng.

Dưới đây là ví dụ về luồng thông tin này. (Chú thích ở dưới cùng bên trái để cho biết khoá công khai và riêng tư).

Hình minh hoạ cách sử dụng khoá máy chủ ứng dụng riêng tư khi gửi một
tin nhắn

"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 gọi tắt là JWT) là một cách gửi thông báo cho bên thứ ba để người nhận có thể xác thực đã gửi tệp.

Khi nhận được thư, bên thứ ba cần phải liên hệ với người gửi và sử dụng khoá này để xác thực chữ ký của JWT. Nếu thì chữ ký đó phải được ký bằng khoá riêng tư, nên phải được gửi từ người gửi dự kiến.

Có nhiều thư viện trên https://jwt.io/ có thể thực hiện việc ký cho bạn. Bạn nên làm việc đó ở nơi bạn 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 đây là 3 chuỗi được kết hợp theo dấu chấm.

Hình minh hoạ các chuỗi trong một trang web JSON
Mã thông báo

Chuỗi đầu tiên và chuỗi thứ hai (Thông tin JWT và dữ liệu JWT) là các đoạn của JSON được mã hoá base64, nghĩa là dữ liệu này có thể đọc đượ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 tin đẩ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. Thao tác này cung cấp thông tin về người gửi JWT, người dự định và khoảng thời gian có hiệu lực.

Đố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à "audience", tức là đối tượng mà JWT nhắm đến. Đối với web, hãy đẩy đối tượng là dịch vụ đẩy, vì vậy chúng tôi đặt đối tượng này thành nguồn gốc của thông báo đẩy .

Giá trị exp là thời hạn của JWT, giúp ngăn chặn những kẻ rình rập sử dụng lại JWT nếu họ chặn nó. Thời gian hết hạn là một dấu thời gian theo giây và không được dài hơn 24 giờ.

Trong Node.js, thời hạn được đặt bằng cách sử dụng:

Math.floor(Date.now() / 1000) + 12 * 60 * 60;

Cần tránh 12 giờ thay vì 24 giờ bất kỳ vấn đề nào về sự khác biệt về đồng hồ giữa ứng dụng gửi và dịch vụ đẩy.

Cuối cùng, giá trị sub cần phải là một URL hoặc một địa chỉ email mailto. Nghĩa là nếu một dịch vụ đẩy cần liên hệ với người gửi, dịch vụ này có thể tìm thấy thông tin liên hệ của JWT. (Đây là lý do tại sao thư viện đẩy web cần có địa chỉ email).

Giống như Thông tin JWT, Dữ liệu JWT được mã hoá dưới dạng base64 an toàn với 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), kết hợp chúng bằng ký tự dấu chấm, chúng ta sẽ gọi "mã thông báo chưa ký" rồi ký tên đó.

Quá trình ký yêu cầu mã hoá "mã thông báo chưa ký" bằng ES256. Theo JWT spec, 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 mã hoá 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 rằng chuỗi được giải mã giống nhau dưới dạng "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 nối bằng dấu chấm) được gửi đến web đẩy dịch vụ dưới dạng tiêu đề Authorization với WebPush sẽ được thêm vào phía trước, tương tự như sau:

Authorization: 'WebPush [JWT Info].[JWT Data].[Signature]';

Giao thức đẩy web cũng nêu rõ khoá máy chủ ứng dụng công khai phải là được gửi trong tiêu đề Crypto-Key dưới dạng chuỗi được mã hoá base64 an toàn với URL bằng p256ecdsa= đã thêm vào trước nội dung này.

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 một 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 thì có thể truy cập vào dữ liệu nhận được.

Một câu hỏi phổ biến được đặt ra từ bất kỳ ai đã sử dụng các dịch vụ đẩy khác là tại sao web đẩy tải trọng dữ liệu có cần được mã hoá không? 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ý.

Điểm hay của hoạt động đẩy trên web là vì tất cả các dịch vụ đẩy đều sử dụng cùng một API (giao thức đẩy web), nhà phát triển không cần quan tâm đến ai đó là dịch vụ đẩy. Chúng tôi có thể đưa ra yêu cầu ở định dạng phù hợp và kỳ vọng thông báo đẩy được gửi đi. Nhược điểm của phương pháp 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. Theo mã hoá tải trọng, thì 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. Việc này giúp bảo vệ .

Phương thức mã hoá tải trọng được định nghĩa trong phần Mã hoá thư .

Trước khi chúng ta xem các bước cụ thể để mã hoá tải trọng thông báo đẩy, chúng ta nên đề cập đến một số kỹ thuật sẽ được sử dụng trong quá trình mã hoá của chúng tôi. (Mời Mat Scales đội mũ rộng rãi để viết bài viết xuất sắc về chủ đề đẩy encryption.)

ECDH và HKDF

Cả ECDH và HKDF đều được sử dụng trong suốt quá trình mã hoá và mang lại các lợi ích cho cho mục đích mã hoá thông tin.

ECDH: Trao đổi khóa 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à riêng tư của riêng mình. Alice và Bob đều chia sẻ khoá công khai với nhau.

Đặ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á khoá riêng tư và khoá công khai của Bob để tạo giá trị bí mật 'X'. Bob có thể làm giống nhau, lấy khoá riêng tư của anh và khoá công khai của Alice để tạo cùng một giá trị "X". Thao tác này tạo thành "X" một bí mật dùng chung còn Alice và Bob chỉ phải chia sẻ khoá công khai. Bob và Alice có thể sử dụng "X" để mã hoá và giải mã thông báo giữa các dịch vụ đó.

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 một bí mật được chia sẻ "X".

Đây là nội dung giải thích tổng quan về ECDH. Nếu bạn muốn tìm hiểu thêm, bạn nên xem video này.

Về mặt mã; hầu hết các ngôn ngữ / nền tảng đều đi kèm với thư viện để giúp dễ dàng tạo ra 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ả 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 biến đổi bất kỳ khoá yếu nào thành vật liệu khoá mạnh được mã hoá. Có thể sử dụng, ví dụ: để chuyển đổi Diffie Hellman đã trao đổi các bí mật được chia sẻ thành tài liệu chính phù hợp để sử dụng trong 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 cụ thể và tăng tính bảo mật.

Quy cách xác định lớp mã hoá này yêu cầu sử dụng SHA-256 làm thuật toán băm của chúng tôi và các khoá kết quả cho HKDF trong công cụ đẩy trên web không được dài quá 256 bit (32 byte).

Trong nút, cách này có thể được 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);
}

Mẹo mũ cho bài viết của Mat Scale cho mã ví dụ này.

Thông tin này bao gồm ECDH một cách lỏng lẻo 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 để thu hút tài liệu không an toàn và đảm bảo an toàn cho tài liệu đó.

Thông tin này sẽ được sử 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 tôi coi là và cách mã hoá thông tin đó.

Thông tin đầu vào

Khi muốn gửi thông báo đẩy đến người dùng có tải trọng, chúng ta cần có ba thông tin đầu vào:

  1. Chính tải trọng.
  2. Mã bí mật auth từ PushSubscription.
  3. Khoá p256dh trong PushSubscription.

Chúng ta thấy các giá trị authp256dh được truy xuất qua PushSubscription nhưng Xin lưu ý rằng 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');

Giá trị auth phải được coi là bí mật và không được chia sẻ ra bên ngoài ứng dụng của bạn.

Khoá p256dh là khoá công khai, đôi khi được gọi là khoá công khai của ứng dụng. Ở đâ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 đã được tạo theo trình duyệt. Trình duyệt sẽ giữ bí mật khoá cá nhân và sử dụng khoá đó để giải mã tải trọng.

Ba giá trị này, auth, p256dhpayload cần thiết làm dữ liệu đầu vào và là kết quả của quá 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ỉ được dùng để mã hoá dữ liệu.

Muối

Dữ liệu ngẫu nhiên cần chứa 16 byte dữ liệu ngẫu nhiên. Trong NodeJS, chúng ta sẽ làm như sau để tạo dữ liệu ngẫu nhiên:

const salt = crypto.randomBytes(16);

Khoá công khai / riêng tư

Khoá công khai và riêng tư phải được tạo bằng đường cong elip P-256, mà chúng ta sẽ thực hiện trong Nút như sau:

const localKeysCurve = crypto.createECDH('prime256v1');
localKeysCurve.generateKeys();

const localPublicKey = localKeysCurve.getPublicKey();
const localPrivateKey = localKeysCurve.getPrivateKey();

Chúng tôi sẽ gọi những khoá này là "khoá cục bộ". Chúng chỉ được dùng để mã hoá và có 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 và với một khoá mới được tạo ngẫu nhiên và tập hợp khoá cục bộ, chúng ta đã sẵn sàng thực hiện một số mã hoá.

Khoá bí mật dùng chung

Bước đầu tiên là tạo một bí mật dùng chung bằng cách sử dụng khoá công khai của gói thuê bao và khóa riêng tư (bạn có nhớ phần giải thích ECDH với Alice và Bob không? Vậy là xong).

const sharedSecret = localKeysCurve.computeSecret(
  subscription.keys.p256dh,
  'base64',
);

Mã này được dùng trong bước tiếp theo để tính Khoá giả ngẫu nhiên (PRK).

Khoá ngẫu nhiên

Khoá ngẫu nhiên (PRK) là tổ hợp tính xác thực của gói thuê bao đẩy bí mật và bí mật chung mà chúng ta vừa tạo ra.

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ì. 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ã thông báo đến và tìm cách mã hoá nội dung dự kiến. \0 thêm một byte có giá trị 0 vào cuối Vùng đệm. Đây là mà các trình duyệt mong đợi khi giải mã thư sẽ mong đợi rất nhiều byte cho mã hoá nội dung, theo sau là một byte có giá trị 0, tiếp theo là dữ liệu đã mã hoá.

Khoá giả mạo của chúng tôi chỉ đơn giản là chạy tính năng xác thực, bí mật được chia sẻ và một phần thông tin mã hoá thông qua HKDF (tức là làm cho mã hoá mạnh hơn).

Bối cảnh

"Ngữ cảnh" là một tập hợp byte dùng để tính hai giá trị sau này trong quá trình mã hoá trình duyệt. Về cơ bản, đó 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ố lượng byte trong khoá công khai của gói thuê bao, theo sau là khoá, rồi đến số byte khoá công khai cục bộ, theo sau là khoá .

Với giá trị ngữ cảnh này, chúng ta có thể 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ị ngăn chặn hoạt động phát lại vì chỉ nên sử 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 tôi.

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, đơn giản là một nội dung chuỗi mã hoá 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 qua HKDF kết hợp dữ liệu ngẫu nhiên 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);

Mã này cung cấp cho chúng tôi số chỉ dùng một lần và khoá mã hoá nội dung.

Thực hiện 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 thuật toán mật mã AES128 bằng khoá mã hoá nội dung vì khoá và số chỉ dùng một lần là vectơ khởi tạo.

Trong Nút, thao tá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 khoảng đệm mà chúng ta muốn để thêm vào phía trước của tải trọng. Lý do chúng ta muốn thêm khoảng đệm là biện pháp này ngăn chặn nguy cơ các kẻ nghe lén có thể xác định "loại" tin nhắn 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 bất kỳ khoảng đệm bổ sung nào.

Ví dụ: nếu bạn 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 đệm, hai byte đầu tiên sẽ có giá trị là 5, do đó, ứng dụng tiêu dùng sau đó 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 sẽ chạy khoảng đệm và tải trọng của mình thông qua thuật toán 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()]);

Bây giờ, chúng ta có tải trọng đã mã hoá. Thật tuyệt vời!

Tất cả việc còn lại là xác định cách tải trọng này được gửi đến dịch vụ đẩy.

Tiêu đề và phần tải trọng đã mã hoá cơ thể

Để 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 của chúng tôi.

Tiêu đề mã hoá

Chế độ 'Mã hoá' tiêu đề phải chứa muối được dùng để mã hoá tải trọng.

Dữ liệu ngẫu nhiên 16 byte phải được mã hoá an toàn cho URL base64 và thêm vào tiêu đề Mã hóa, như sau:

Encryption: salt=[URL Safe Base64 Encoded Salt]

Tiêu đề khoá mã hoá

Chúng ta thấy tiêu đề Crypto-Key được dùng trong "Application Server Key" (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 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]

Loại nội dung, thời lượng và tiêu đề mã hoá

Tiêu đề Content-Length là số byte trong mã hoá tải trọng. "Loại nội dung" và "Content-Encoding" tiêu đề là các giá trị cố định. Thông tin này được hiển thị bên dưới.

Content-Length: [Number of Bytes in Encrypted Payload]
Content-Type: 'application/octet-stream'
Content-Encoding: 'aesgcm'

Khi đã thiết lập các tiêu đề này, chúng ta cần gửi tải trọng đã mã hoá dưới dạng phần nội dung về yêu cầu của chúng tôi. Lưu ý rằng Content-Type được đặt thành application/octet-stream Lý do là tải trọng được 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();

Tiêu đề khác?

Chúng ta đã đề cập đến các tiêu đề được dùng cho JWT / Khoá máy chủ ứng dụng (tức là cách xác định bằng dịch vụ đẩy) và chúng ta đã đề cập đến các tiêu đề dùng để gửi mã hoá tải trọng.

Có các tiêu đề bổ sung mà các dịch vụ đẩy sử dụng để thay đổi hành vi của tin nhắn đã gửi. Một số tiêu đề trong số này là bắt buộc, trong khi các tiêu đề khác là không bắt buộc.

Tiêu đề TTL

Bắt buộc

TTL (hay 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 của mình hoạt động trên dịch vụ đẩy trước khi nó được đã gửi. Khi TTL hết hạn, thông báo này sẽ bị xoá khỏi đẩy hàng đợi dịch vụ và nó sẽ không được phân phối.

TTL: [Time to live in seconds]

Nếu bạn đặt TTL bằng 0, dịch vụ đẩy sẽ cố gắng phân phố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ị, tin nhắn của bạn sẽ bị loại bỏ ngay lập tức khỏi hàng đợi 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 liệu đ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ể được dùng để thay thế thông báo đang chờ xử lý bằng thông báo mới nếu chúng có tên chủ đề phù hợp.

Điều này rất hữu ích trong trường hợp nhiều thư được gửi trong khi thiết bị của bạn đang ngoại tuyến và bạn thực sự chỉ muốn người dùng thấy dữ liệu 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 một thông báo đối với người dùng. Chiến dịch này có thể được dịch vụ đẩy sử dụng chỉ nhằm giúp tiết kiệm pin thiết bị của người dùng nhận thông báo quan trọng khi pin yếu.

Giá trị tiêu đề được định nghĩa như dưới đây. Mặc định có giá trị là normal.

Urgency: [very-low | low | normal | high]

Mọi thứ ở cùng một nơi

Nếu có thêm câu hỏi về cách hoạt động của tính năng này, bạn luôn có thể xem cách thư viện kích hoạt thông báo đẩy trên web-push-libs org.

Khi có tải trọng được mã hoá cũng như các tiêu đề ở trên, bạn chỉ cần thực hiện yêu cầu POST vào endpoint trong PushSubscription.

Vậy chúng ta 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 tạo yêu cầu đối với một dịch vụ đẩy, bạn cần kiểm tra mã trạng thái của phản hồi vì thông tin đó sẽ cho bạn biết liệu yêu cầu có thành công hay khô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. Tức là máy chủ ứng dụng của bạn đã đạt đến tỷ lệ bằng dịch vụ đẩy. Dịch vụ đẩy phải có chế độ "Thử lại sau" để cho biết khoảng 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. Đây là dấu hiệu cho biết gói thuê bao đã hết hạn và không dùng được. Trong trường hợp này, bạn nên xoá "PushSubscription" và đợi máy khách đăng ký lại người dùng.
410 Không tồn tại. Gói thuê bao không còn hiệu lực nữa và cần phải xoá từ máy chủ ứng dụng. Có thể tái tạo thao tác 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à một dịch vụ đẩy phải hỗ trợ là 4096 byte (hoặc 4kb).

Điểm đến tiếp theo

Phòng thí nghiệm lập trình