更改推送管理器订阅中的应用程序服务器密钥

问题描述 投票:0回答:5

我使用 Service Worker 实现了网络推送通知。我使用特定的应用程序服务器密钥收集了用户订阅。假设如果我们更改应用程序服务器密钥,那么当我们使用“reg.pushManager.getSubscription()”获取订阅时,我们将获取使用旧应用程序服务器密钥创建的旧订阅信息。如何处理这种情况?如何获取用户的新订阅?

push-notification service-worker firebase-notifications
5个回答
7
投票

使用

reg.pushManager.getSubscription()
获取订阅并检查当前订阅是否使用新的应用程序服务器密钥。如果没有,则对现有订阅调用
unsubscribe()
函数并再次重新订阅。


5
投票

正确启动 Service Worker 并获得权限后,调用

navigator.serviceWorker.ready
即可访问
*.pushManager
对象。

从这个对象中,我们调用另一个 Promise 来获取我们真正关心的

pushSubscription
对象。

如果用户从未订阅过,

pushSubscription
将是
null
,否则我们会从中获取密钥并检查它是否不同,如果是这种情况,我们取消订阅用户并再次订阅他们。

var NEW_PUBLIC_KEY = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
    
Notification.requestPermission(function (result) {
    if (permissionResult == 'granted'){
        subscribeUser();
    }
});
    
function subscribeUser() {
    navigator.serviceWorker.ready
    .then(registration => {
        registration.pushManager.getSubscription()
        .then(pushSubscription => {
            if(!pushSubscription){
                //the user was never subscribed
                subscribe(registration);
            }
            else{
                //check if user was subscribed with a different key
                let json = pushSubscription.toJSON();
                let public_key = json.keys.p256dh;
                
                console.log(public_key);
                
                if(public_key != NEW_PUBLIC_KEY){
                    pushSubscription.unsubscribe().then(successful => {
                        // You've successfully unsubscribed
                        subscribe(registration);
                    }).catch(e => {
                        // Unsubscription failed
                    })
                }
            }
        });
    })
}

function subscribe(registration){
    registration.pushManager.subscribe({
        userVisibleOnly: true,
        applicationServerKey: urlBase64ToUint8Array(NEW_PUBLIC_KEY)
    })
    .then(pushSubscription => {
        //successfully subscribed to push
        //save it to your DB etc....
    });
}

function urlBase64ToUint8Array(base64String) {
    var padding = '='.repeat((4 - base64String.length % 4) % 4);
    var base64 = (base64String + padding)
        .replace(/\-/g, '+')
        .replace(/_/g, '/');

    var rawData = window.atob(base64);
    var outputArray = new Uint8Array(rawData.length);

    for (var i = 0; i < rawData.length; ++i) {
        outputArray[i] = rawData.charCodeAt(i);
    }
    return outputArray;
}

0
投票

就我而言,我设法通过清除缓存和cookie来解决这个问题


0
投票

您通过调用

sub.getKey('p256dh')
(或
sub.toJSON.keys.p256dh
)获得的密钥是客户端的公钥,它始终与服务器公钥不同。您需要比较新的服务器公钥和
sub.options.applicationServerKey

以上

sub
reg.pushManager.getSubscription()
已解决的承诺。

因此:

  1. 获取 PushSubscription 接口 -
    reg.pushManager.getSubscription().then(sub => {...})
    ,如果 sub 为 null,则不存在订阅,因此不用担心,但如果定义了:
  2. 在块内获取当前使用的密钥
    sub.options.applicationServerKey
  3. 将其转换为字符串,因为你无法直接比较 ArrayBuffer -
    const curKey = btoa(String.fromCharCode.apply(null, new Uint8Array(sub.options.applicationServerKey)))
  4. 将其与您的新钥匙进行比较。如果密钥不同,请调用
    sub.unsubscribe()
    ,然后通过调用
    reg.pushManager.subscribe(subscribeOptions)
    再次订阅,其中 subscribeOptions 使用您的新密钥。您在 PushSubscription 上调用“取消订阅”,但在 PushManager 上调用
    subscribe

0
投票

这是用于处理比较应用程序服务器密钥更改的真实代码。

我留下了更多如何处理的细节。 以前的答案对我来说还不够。

function urlBase64ToUint8Array(base64String) {
  const padding = "=".repeat((4 - (base64String.length % 4)) % 4);
  const base64 = (base64String + padding).replace(/-/g, "+").replace(/_/g, "/");

  const rawData = window.atob(base64);
  const outputArray = new Uint8Array(rawData.length);

  for (let i = 0; i < rawData.length; ++i) {
    outputArray[i] = rawData.charCodeAt(i);
  }
  return outputArray;
}

function subscribe(registration) {
  registration.pushManager
    .subscribe({
      userVisibleOnly: true,
      applicationServerKey: urlBase64ToUint8Array(APPLICATON_SERVER_KEY),
    })
    .then(async function (pushSubscription) {
      //successfully subscribed to push
      //save it to your DB etc....
    });
}
navigator.serviceWorker.ready.then(function (registration) {
  registration.pushManager.getSubscription().then(function (subscription) {
    if (!subscription) {
      return subscribe(registration);
    }

    const applicationServerKey = btoa(
      String.fromCharCode.apply(
        null,
        new Uint8Array(subscription.options.applicationServerKey)
      )
    );
    const currentKey = applicationServerKey
      .replaceAll("+", "-")
      .replaceAll("/", "_")
      .replaceAll("=", "");
    if (currentKey.indexOf(APPLICATON_SERVER_KEY) > -1) {
      console.log("Already subscribed");
      return subscription;
    } else {
      subscription
        .unsubscribe()
        .then(() => {
          console.log("subscribe again because of different key");
          return subscribe(registration);
        })
        .catch(function (e) {
          console.log("unsubscribed error", e);
        });
    }
  });
});

当您更改 applicationServerKey 时,后端将通过端点发送推送消息而收到响应 401 或 403 错误。

所以你需要在服务器端处理它。

我用这段代码完成了。

for subscription in SubscriptionInfo.objects.all():
    try:
        send_to_subscription(subscription, payload)
    except Exception as e:
        if e.response.status_code == 403 or e.response.status_code == 401:
            subscription.delete()
        print(e)
        pass
© www.soinside.com 2019 - 2024. All rights reserved.