當 Chrome 首次支援 Web Push API 時,它會依賴 Firebase 雲端通訊 (FCM) (前稱為 Google 雲端通訊 (GCM)) 推播服務。這需要使用專屬 API。這可讓 Chrome 在 Web Push 通訊協定規格仍在撰寫時,向開發人員提供 Web Push API,並在 Web Push 通訊協定缺少驗證機制時,提供驗證機制。好消息:這兩種情況都不再適用。
FCM / GCM 和 Chrome 現已支援標準的網路推播通訊協定,而發送端驗證功能則可透過實作 VAPID 達成,這表示您的網路應用程式不再需要「gcm_sender_id」。
在本文中,我將首先說明如何轉換現有的伺服器程式碼,以便使用 Web Push 通訊協定搭配 FCM。接下來,我會說明如何在用戶端和伺服器程式碼中實作 VAPID。
FCM 支援網頁推送通訊協定
首先,讓我們先瞭解一下背景。當網頁應用程式註冊推送訂閱項目時,系統會提供推送服務的網址。您的伺服器將透過網頁應用程式使用這個端點,將資料傳送給您的使用者。如果您在 Chrome 中訂閱沒有 VAPID 的使用者,系統會提供 FCM 端點。(我們稍後會介紹 VAPID)。在 FCM 支援網路推送通訊協定之前,您必須先從網址結尾擷取 FCM 註冊 ID 並放入標頭中,才能提出 FCM API 要求。舉例來說,FCM 端點是 https://android.googleapis.com/gcm/send/ABCD1234
,註冊 ID 則為「ABCD1234」。
由於 FCM 支援 Web Push Protocol,您可以保留端點,並使用網址做為 Web Push Protocol 端點。(這可讓它與 Firefox 保持一致,並且希望未來所有瀏覽器都會採用這項功能)。
在深入探討 VAPID 之前,我們需要確保伺服器程式碼能正確處理 FCM 端點。以下示範如何向節點中的推送服務發出要求。請注意,我們會將 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 是「自願應用程式伺服器識別」的新簡稱。這個新規格基本上定義了應用程式伺服器與推送服務之間的握手,並允許推送服務確認哪個網站正在傳送訊息。有了 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 標頭的一般 Web Push Protocol 要求:Authorization 標頭和 Crypto-Key 標頭。
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
請注意以下幾點。首先,Authorization 標頭實際上包含「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
。