當 Chrome 首次支援 Web Push API 時,它會依賴 Firebase 雲端通訊 (FCM) (前稱為 Google 雲端通訊 (GCM)) 推播服務。這需要使用專屬 API。這可讓 Chrome 在 Web Push 通訊協定規格仍在撰寫時,向開發人員提供 Web Push API,並在 Web Push 通訊協定缺少驗證機制時,提供驗證機制。好消息:這兩種情況都不再適用。
FCM / GCM 和 Chrome 現已支援標準的網路推播通訊協定,而傳送者驗證功能則可透過實作 VAPID 達成,這表示您的網路應用程式不再需要「gcm_sender_id」。
在本文中,我將首先說明如何轉換現有的伺服器程式碼,以便搭配 FCM 使用 Web Push 通訊協定。接下來,我會說明如何在用戶端和伺服器程式碼中實作 VAPID。
FCM 支援 Web Push 通訊協定
首先,讓我們先瞭解一下背景。當網頁應用程式註冊推播訂閱項目時,系統會提供推播服務的網址。您的伺服器會使用這個端點,透過您的網路應用程式將資料傳送給使用者。如果您在 Chrome 中訂閱使用者而未提供 VAPID,系統會提供 FCM 端點。(我們稍後會介紹 VAPID)。在 FCM 支援 Web Push Protocol 之前,您必須從網址結尾擷取 FCM 註冊 ID,並將其放在標頭中,再提出 FCM API 要求。舉例來說,https://android.googleapis.com/gcm/send/ABCD1234
的 FCM 端點註冊 ID 為「ABCD1234」。
由於 FCM 支援 Web Push Protocol,您可以保留端點,並使用網址做為 Web Push Protocol 端點。(這可讓它與 Firefox 保持一致,並且希望未來所有瀏覽器都會採用這項功能)。
在深入探討 VAPID 之前,我們需要確保伺服器程式碼能正確處理 FCM 端點。以下是 Node 中向推播服務提出要求的範例。請注意,我們會將 API 金鑰新增至 FCM 要求標頭。其他推播服務端點則不需要這麼做。對於 52 以下版本的 Chrome、Opera Android 和 Samsung Browser,您仍需要在網頁應用程式的 manifest.json 中加入「gcm_sender_id」。API 金鑰和傳送者 ID 可用於檢查提出要求的伺服器是否實際允許傳送訊息給收件者。
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');
}
});
請注意,這是 FCM / GCM API 的變更,因此您不需要更新訂閱項目,只要變更伺服器程式碼,即可定義上述的標頭。
推出用於伺服器識別的 VAPID
VAPID 是「Voluntary Application Server Identification」的簡稱,這個新規格基本上會定義應用程式伺服器與推播服務之間的握手程序,並允許推播服務確認哪個網站傳送訊息。有了 VAPID,您就不必執行 FCM 特定的傳送推播訊息步驟。您不再需要 Firebase 專案、gcm_sender_id
或 Authorization
標頭。
這個程序非常簡單:
- 應用程式伺服器會建立公開/私密金鑰組。公開金鑰會提供給您的網頁應用程式。
- 當使用者選擇接收推播時,請將公開金鑰新增至 subscribe() 呼叫的選項物件。
- 應用程式伺服器傳送推播訊息時,請一併附上已簽署的 JSON Web Token 和公開金鑰。
讓我們詳細瞭解這些步驟。
建立公開/私密金鑰組
我對加密技術一竅不通,因此以下是規格中有關 VAPID 公開/私密金鑰格式的相關部分:
應用程式伺服器應產生並維護可透過橢圓曲線數位簽章 (ECDSA) 在 P-256 曲線上使用的簽署金鑰組。
如需操作說明,請參閱 web-push 節點程式庫:
function generateVAPIDKeys() {
var curve = crypto.createECDH('prime256v1');
curve.generateKeys();
return {
publicKey: curve.getPublicKey(),
privateKey: curve.getPrivateKey(),
};
}
使用公開金鑰訂閱
如要讓 Chrome 使用者訂閱 VAPID 公開金鑰的推播,您必須使用 subscribe() 方法的 applicationServerKey
參數,將公開金鑰做為 Uint8Array 傳遞。
const publicKey = new Uint8Array([0x4, 0x37, 0x77, 0xfe, …. ]);
serviceWorkerRegistration.pushManager.subscribe(
{
userVisibleOnly: true,
applicationServerKey: publicKey
}
);
您可以檢查結果訂閱物件中的端點,瞭解是否已成功運作。如果來源為 fcm.googleapis.com
,表示已成功運作。
https://fcm.googleapis.com/fcm/send/ABCD1234
傳送推送訊息
如要使用 VAPID 傳送訊息,您必須建立一般網路推播通訊協定要求,並附加兩個 HTTP 標頭:授權標頭和加密金鑰標頭。
Authorization 標頭
Authorization
標頭是已簽署的 JSON Web Token (JWT),前面有「WebPush」。
JWT 是一種與第二方分享 JSON 物件的方式,讓發送方可以簽署,而接收方可以驗證簽署是否來自預期的寄件者。JWT 的結構是三個經過加密的字串,中間以一個點連接。
<JWTHeader>.<Payload>.<Signature>
JWT 標頭
JWT 標頭包含用於簽署的演算法名稱和符記類型。對於 VAPID,必須符合以下條件:
{
"typ": "JWT",
"alg": "ES256"
}
接著會以 base64 網址編碼,並形成 JWT 的第一部分。
酬載
酬載是另一個 JSON 物件,其中包含下列項目:
- 目標對象 (「aud」)
- 這是推播服務的來源 (不是網站的來源)。在 JavaScript 中,您可以執行下列操作來取得目標對象:
const audience = new URL(subscription.endpoint).origin
- 這是推播服務的來源 (不是網站的來源)。在 JavaScript 中,您可以執行下列操作來取得目標對象:
- 到期時間 (「exp」)
- 這是要求應視為逾時的秒數。這項作業必須在提出要求後的 24 小時內完成 (以 UTC 時間為準)。
- 主旨 (「sub」)
- 主旨必須是網址或
mailto:
網址。這樣一來,如果推播服務需要與郵件寄件者聯絡,就會有聯絡人可供使用。
- 主旨必須是網址或
酬載範例如下所示:
{
"aud": "http://push-service.example.com",
"exp": Math.floor((Date.now() / 1000) + (12 * 60 * 60)),
"sub": "mailto: my-email@some-url.com"
}
這個 JSON 物件採用 base64 網址編碼,並構成 JWT 的第二部分。
簽名
簽名是將經過編碼的標頭和酬載連結在一起的結果,然後使用先前建立的 VAPID 私密金鑰加密結果。結果本身應附加至標頭,並加上一個點。
我不會提供這方面的程式碼範例,因為有許多程式庫會採用標頭和酬載 JSON 物件,並為您產生此簽章。
已簽署的 JWT 會用作授權標頭,並在前面加上「WebPush」,看起來會像這樣:
WebPush eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJodHRwczovL2ZjbS5nb29nbGVhcGlzLmNvbSIsImV4cCI6MTQ2NjY2ODU5NCwic3ViIjoibWFpbHRvOnNpbXBsZS1wdXNoLWRlbW9AZ2F1bnRmYWNlLmNvLnVrIn0.Ec0VR8dtf5qb8Fb5Wk91br-evfho9sZT6jBRuQwxVMFyK5S8bhOjk8kuxvilLqTBmDXJM5l3uVrVOQirSsjq0A
請注意以下幾點。首先,授權標頭會包含「WebPush」字詞,後面接著一個空格,然後是 JWT。請注意,JWT 標頭、酬載和簽章之間以圓點分隔。
Crypto-Key 標頭
除了授權標頭之外,您還必須將 VAPID 公開金鑰新增至 Crypto-Key
標頭,做為 base64 url 編碼字串,並在前面加上 p256ecdsa=
。
p256ecdsa=BDd3_hVL9fZi9Ybo2UUzA284WG5FZR30_95YeZJsiApwXKpNcF1rRPF3foIiBHXRdJI2Qhumhf6_LFTeZaNndIo
當您傳送含有加密資料的通知時,您會使用 Crypto-Key
標頭,因此如要新增應用程式伺服器金鑰,只需在新增上述內容前加上分號即可:
dh=BGEw2wsHgLwzerjvnMTkbKrFRxdmwJ5S_k7zi7A1coR_sVjHmGrlvzYpAT1n4NPbioFlQkIrTNL8EH4V3ZZ4vJE;
p256ecdsa=BDd3_hVL9fZi9Ybo2UUzA284WG5FZR30_95YeZJsiApwXKpNcF1rRPF3foIiBHXRdJI2Qhumhf6_LFTeZaN
這些異動的實際影響
有了 VAPID,您就無須再註冊 GCM 帳戶,即可在 Chrome 中使用推播功能,而且您可以使用相同的程式碼路徑,在 Chrome 和 Firefox 中訂閱使用者,並傳送訊息給使用者。兩者都符合標準。
請注意,在 Chrome 51 以下版本、Opera for Android 和 Samsung 瀏覽器中,您仍需要在網頁應用程式資訊清單中定義 gcm_sender_id
,並在要傳回的 FCM 端點中新增授權標頭。
VAPID 可讓您避免使用這些專屬規定。如果您導入 VAPID,這項功能將可在所有支援網路推播的瀏覽器中運作。隨著越來越多瀏覽器支援 VAPID,您可以決定何時從資訊清單中移除 gcm_sender_id
。