用于Firebase云消息传递的服务器密钥的Base64编码

时间:2016-12-27 02:12:33

标签: firebase web-applications push-notification firebase-cloud-messaging

自从3天以来我一直陷入这个问题并搜索谷歌,但没有成功。我按照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中提供的其他密钥,但没有成功。请告诉我实际使用哪个服务器密钥以及如何使用?

我正在附加我实际执行推送订阅的代码段。

 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';
  }
 });
});
}

2 个答案:

答案 0 :(得分:3)

令人遗憾的是,这与Chrome有点令人困惑。

在最基本的层面:网络推送完全独立于网站的Firebase 消息传递。

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 Messaging 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拥有自己的网络推送SDK,如果你使用它,那么你应该按照它的指南使用它的服务器端API。
  • Web Push(开放式Web标准)与Firebase(或任何其他推送服务)没有直接关系,因此您可以创建特定格式的密钥并使用Web推送协议来触发推送消息。 (Chrome在幕后使用Firebase,但作为网络推送网站的开发者,您无需为Firebase执行任何操作。)

答案 1 :(得分:0)

了解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)。