Firebase 云消息传递服务器密钥的 Base64 编码
Posted
技术标签:
【中文标题】Firebase 云消息传递服务器密钥的 Base64 编码【英文标题】:Base64 Encoding of Server Key for Firebase Cloud Messaging 【发布时间】:2016-12-27 02:12:32 【问题描述】:自 3 天以来,我一直陷入这个问题并搜索了 Google,但没有成功。我按照Push Notifications Example 中的说明进行操作。但是当我尝试实现它时,我得到了这个讨厌的错误。
Uncaught (in promise) DOMException: Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.
我发现 Base64 字符串中不允许使用冒号 (:),但在 Cloud Messenging 选项卡中的 Firebase 中给出的服务器密钥是
AAAA-8MWvk0:APA91bHmU8XL-rzOn9SPbpG98NSqAJcuhYBBh7oze_XhprBpK7Q9PPWm3PwBo6Llxby4zjhVtgvKPIaSAQGp-8RfMV10_d1GekzICrVX9oYO8pi6dOM4VTp52CCAzug6NYIa10tNddsgE2P5QowGAYcnRHxLkrHXsw
其中包含一个冒号(别担心,它只是一个测试应用程序,所以隐私没有问题)。
当我尝试使用旧版服务器密钥时,它只会引发错误。我也尝试使用 Firebase 中提供的其他键,但没有成功。请告诉我实际使用哪个服务器密钥以及如何使用?
我附上了我的代码 sn-p 实际执行推送订阅。
const API_KEY = "AIzaSyByIOl-mW0pu8SEXFeutB8jq59hhiau0wI";
var GCM_ENDPOINT = 'https://fcm.googleapis.com/fcm/send';
const legacy = 'AIzaSyDGF8t125bJ4wBvYn_UdRewkTxHGr7KpH8';
const applicationServerPublicKey = 'AAAA-8MWvk0APA91bHmU8XL-rzOn9SPbpG98NSqAJcuhYBBh7oze_XhprBpK7Q9PPWm3PwBo6Llxby4zjhVtgvKPIaSAQGp-8RfMV10_d1GekzICrVX9oYO8pi6dOM4VTp52CCAzug6NYIa10tNddsgE2P5QowGAYcnRHxLkrHXsw';
function urlB64ToUint8Array(base64String)
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding)
.replace(/\-/g, '+')
.replace(/_/g, '/');
console.log(base64);
const rawData = window.atob(base64);
console.log(rawData);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i)
outputArray[i] = rawData.charCodeAt(i);
return outputArray;
function endpointWorkaround(pushSubscription)
// Make sure we only mess with GCM
if(pushSubscription.endpoint.indexOf('https://fcm.googleapis.com/fcm/send') !== 0)
return pushSubscription.endpoint;
var mergedEndpoint = pushSubscription.endpoint;
// Chrome 42 + 43 will not have the subscriptionId attached
// to the endpoint.
if (pushSubscription.subscriptionId &&
pushSubscription.endpoint.indexOf(pushSubscription.subscriptionId) === -1)
// Handle version 42 where you have separate subId and Endpoint
mergedEndpoint = pushSubscription.endpoint + '/' +
pushSubscription.subscriptionId;
return mergedEndpoint;
function sendSubscriptionToServer(subscription)
// TODO: Send the subscription.endpoint
// to your server and save it to send a
// push message at a later date
//
// For compatibly of Chrome 43, get the endpoint via
// endpointWorkaround(subscription)
console.log('TODO: Implement sendSubscriptionToServer()', JSON.stringify(subscription));
var mergedEndpoint = endpointWorkaround(subscription);
// This is just for demo purposes / an easy to test by
// generating the appropriate cURL command
var temp = showCurlCommand(mergedEndpoint);
return temp;
// NOTE: This code is only suitable for GCM endpoints,
// When another browser has a working version, alter
// this to send a PUSH request directly to the endpoint
function showCurlCommand(mergedEndpoint)
// The curl command to trigger a push message straight from GCM
if (mergedEndpoint.indexOf(GCM_ENDPOINT) !== 0)
console.warn('This browser isn\'t currently ' + 'supported for this demo');
return;
var endpointSections = mergedEndpoint.split('/');
var subscriptionId = endpointSections[endpointSections.length - 1];
var curlCommand = 'curl --header "Authorization: key=' + API_KEY + '" --header Content-Type:"application/json" ' + GCM_ENDPOINT + ' -d "\\"registration_ids\\":[\\"' + subscriptionId + '\\"]"';
console.log(curlCommand);
return subscriptionId;
function initialiseState()
// Are Notifications supported in the service worker?
if (!('showNotification' in ServiceWorkerRegistration.prototype))
console.warn('Notifications aren\'t supported.');
return;
// Check the current Notification permission.
// If its denied, it's a permanent block until the
// user changes the permission
if (Notification.permission === 'denied')
console.warn('The user has blocked notifications.');
return;
// Check if push messaging is supported
if (!('PushManager' in window))
console.warn('Push messaging isn\'t supported.');
return;
var prom = new Promise(function(resolve, reject)
navigator.serviceWorker.ready.then(function(serviceWorkerRegistration)
// Do we already have a push message subscription?
serviceWorkerRegistration.pushManager.getSubscription().then(function(subscription)
// Enable any UI which subscribes / unsubscribes from
// push messages.
// var pushButton = document.querySelector('.js-push-button');
// pushButton.disabled = false;
if (!subscription)
// We aren’t subscribed to push, so set UI
// to allow the user to enable push
subscribe();
return;
// Keep your server in sync with the latest subscription
var temp = sendSubscriptionToServer(subscription);
if(temp)
resolve(temp);
else
reject("Oops!")
// Set your UI to show they have subscribed for
// push messages
// pushButton.textContent = 'Disable Push Messages';
// isPushEnabled = true;
)
.catch(function(err)
console.error('Error during getSubscription()', err);
reject(err);
);
);
);
return prom;
function unsubscribe()
// var pushButton = document.querySelector('.js-push-button');
// pushButton.disabled = true;
navigator.serviceWorker.ready.then(function(serviceWorkerRegistration)
// To unsubscribe from push messaging, you need get the
// subcription object, which you can call unsubscribe() on.
serviceWorkerRegistration.pushManager.getSubscription().then(
function(pushSubscription)
// Check we have a subscription to unsubscribe
if (!pushSubscription)
// No subscription object, so set the state
// to allow the user to subscribe to push
// isPushEnabled = false;
// pushButton.disabled = false;
// pushButton.textContent = 'Enable Push Messages';
return;
// TODO: Make a request to your server to remove
// the users data from your data store so you
// don't attempt to send them push messages anymore
// We have a subcription, so call unsubscribe on it
pushSubscription.unsubscribe().then(function()
// pushButton.disabled = false;
// pushButton.textContent = 'Enable Push Messages';
// isPushEnabled = false;
).catch(function(e)
// We failed to unsubscribe, this can lead to
// an unusual state, so may be best to remove
// the subscription id from your data store and
// inform the user that you disabled push
console.log('Unsubscription error: ', e);
// pushButton.disabled = false;
);
).catch(function(e)
console.error('Error thrown while unsubscribing from ' + 'push messaging.', e);
);
);
function subscribe()
// Disable the button so it can't be changed while
// we process the permission request
// var pushButton = document.querySelector('.js-push-button');
// pushButton.disabled = true;
navigator.serviceWorker.ready.then(function(serviceWorkerRegistration)
const applicationServerKey = urlB64ToUint8Array(applicationServerPublicKey);
serviceWorkerRegistration.pushManager.subscribe(userVisibleOnly: true, applicationServerKey: applicationServerKey)
.then(function(subscription)
console.log(subscription);
// The subscription was successful
// isPushEnabled = true;
// pushButton.textContent = 'Disable Push Messages';
// pushButton.disabled = false;
// TODO: Send the subscription subscription.endpoint
// to your server and save it to send a push message
// at a later date
return sendSubscriptionToServer(subscription);
)
.catch(function(e)
if (Notification.permission === 'denied')
// The user denied the notification permission which
// means we failed to subscribe and the user will need
// to manually change the notification permission to
// subscribe to push messages
console.log('Permission for Notifications was denied');
// pushButton.disabled = true;
else
// A problem occurred with the subscription, this can
// often be down to an issue or lack of the gcm_sender_id
// and / or gcm_user_visible_only
console.log('Unable to subscribe to push.', e);
// pushButton.disabled = false;
// pushButton.textContent = 'Enable Push Messages';
);
);
【问题讨论】:
我不熟悉使用 Web 应用程序的推送通知,但是在您指出的 tutorial 中,并没有真正说applicationServerPublicKey
应该与服务器的值相同Firebase 控制台中的密钥。他们只指出您应该使用此page 生成一个。
是的,我知道。但是我的生产应用程序使用他们的推送服务 Firebase,所以所有推送消息都在那里生成。所以如果我想使用 Firebase,那么我应该在这里为 applicationServerPublickey
替换哪个键?
【参考方案1】:
可悲的是,这对 Chrome 来说是一件令人困惑的事情。
在最基本的层面上:Web Push 与 Firebase 完全分开 Web 消息传递。
Web 推送和应用服务器密钥
Web 推送需要一个应用服务器密钥,这是您传递给服务工作者推送管理器的订阅调用的内容:
pushManager.subscribe(userVisibleOnly: true, applicationServerKey: MY_KEY);
在网络推送中,applicationServerKey
需要是一个 Uint8Array(即 65 个字节的数组)。您可以随意生成此密钥,只需确保将私钥保密并在您的网络应用程序中使用公钥。
在我编写的代码实验室中,我指示您从以下位置获取密钥:https://web-push-codelab.appspot.com/
此页面动态生成一组应用服务器密钥并将结果存储在本地存储中(因此您可以使用同一页面发送推送消息 - 这需要该应用服务器密钥)。
您可以通过web-push's node CLI 等工具轻松获取密钥。
Firebase 服务器密钥
适用于 Web 的 Firebase 消息传递 SDK 使用服务工作人员在 Web 上实现推送,但它会为您管理应用服务器密钥。原因是 Firebase Messaging SDK 设置您使用 Firebase Cloud Message API 来触发推送消息,而不是您需要在您正在处理的代码实验室中使用的 Web 推送协议。
Firebase 控制台中的“服务器密钥”是您在触发推送消息时使用的密钥,它是“授权”标头。
https://fcm.googleapis.com/fcm/send
Content-Type: application/json
Authorization: key=<YOUR SERVER KEY HERE>
总结
Firebase Messaging 有自己的用于 Web 推送的 SDK,如果您使用它,那么您应该遵循它的指南并使用它的服务器端 API。 Web 推送(开放式 Web 标准)与 Firebase(或任何其他推送服务)没有直接依赖关系,因此您可以创建特定格式的密钥并使用 Web 推送协议来触发推送消息。 (Chrome 在幕后使用 Firebase,但作为网络推送网站的开发者,您无需为 Firebase 做任何事情。【讨论】:
您的说明链接不通。 抱歉,应用引擎似乎让我失望了。 是否可以将 FCM 与本机 Web 推送 API 一起使用。即不使用 FCM javascript sdk?我想所有本机 API 需要的是 FCM 允许您下载的 FCM 公钥。那是对的吗? @Matt Gaunt web-push-codelab.appspot.com 消失了。 404错误 @MattGaunt 也许你可以验证我上面的回答,因为你比我更有权威!【参考方案2】:了解 FCM 推送服务和 FCM Firebase 产品之间的区别很重要。
FCM 推送服务
负责将推送消息传递到 chrome 浏览器。推送消息到达 Chrome 浏览器的唯一方法是通过 FCM 推送服务。 FCM 推送服务位于mozilla push service 旁边。每个浏览器都有自己的推送服务。 推送服务实现web push protocol。推送服务的Chrome浏览器组件向推送服务的后端组件,后端组件maintains an open HTTP2 connection with the browser发出请求,并使用HTTP2 server push向浏览器传递推送消息。 在 chrome 上调用pushManager.subscribe()
将为您提供https://fcm.googleapis.com/fcm/send/f4wa5jqguZI:APA91bEI0J6gtg3NYqdB_NhB-YWUPxYVdWkK25h0X2nRogeeMYW3INS71hqyAOG06bvyqwbGhukl9qiJJHU9IalxcfLOE47PN70qwKwcBgbDSEW-NFYkQqv3Uj-qANZT4zQ3kG76asdf
形式的端点。
FCM Firebase 产品
Firebase 产品使用 FCM 推送服务将推送消息传送到 Chrome 浏览器。然而,firebase 产品可以与其他推送服务和其他浏览器进行交互。 firebase 产品甚至可以通过APNS 向 ios 应用发送推送消息,这不是基于 Web 推送协议的。 调用firebase.messaging().getToken()
会给您一个eyFOxmzsVFGTwi80wpAcR8:APA91bEtIDf2SH4D1CNcQ2M3kX9-i5nHsbsTXcvO7UJaWteXQz7wxTxNvpmHewMpaMeXf90_BsAJblVpsVoQAUT-CIxTUAkJOr4X3LEp0SbK8E1_iRrpd55e5giP3-r3jDN0rxDuasdf
形式的令牌(有时称为registrationID)。
【讨论】:
以上是关于Firebase 云消息传递服务器密钥的 Base64 编码的主要内容,如果未能解决你的问题,请参考以下文章
在哪里可以找到 Firebase 云消息传递的 API KEY?
FCM(Firebase云消息)中的服务器密钥和旧服务器密钥有啥区别