使用VAPID进行网络推送:400/401未经授权的注册

首先,我已经检查了这些问题,没有任何运气:

  • Web Push API Chrome,返回“未经授权的注册”
  • [WebPush] [VAPID]请求失败,400 UnauthorizedRegistration

我正在尝试为我正在处理的Web应用程序实现Web推送通知。 目前,我已达到以下目标:

  • 创建VAPID密钥对( 使用本指南 )。
  • 注册服务工作者(只有服务工作者,我认为不再需要manifest.json )。
  • 将用户订阅到服务器(订阅数据将存储在数据库中)。
  • 使用webpush gem发送推送通知。

设置完成后,一切正常(在localhost和远程计算机上)。 但是,在几个小时后(12到24小时之间),通知将停止在远程计算机上工作(localhost工作正常)。 在此之后,从服务器端(Rails)发送推送通知时会引发以下错误:

  • Chrome: UnauthorizedRegistration 400 (无额外信息)。
  • Firefox: {"code": 401, "errno": 109, "error": "Unauthorized", "more_info": "http://autopush.readthedocs.io/en/latest/http.html#error-codes", "message": "Request did not validate Invalid bearer token: Auth > 24 hours in the future"}

出现此错误后,我尝试取消订阅并在每次访问页面时重新订阅用户。 重新订阅完成后,db上的订阅字段会更新,但仍会抛出错误,并显示相同的信息。 浏览器和服务工作者都不会抛出任何错误。

我试图通过在Chrome Dev Tools控制台上放置一些js代码,取消注册服务工作者,并重置推送通知权限来手动强制重新订阅,但没有解决错误。 我只能通过创建重新启动远程计算机的新VAPID密钥对来修复此错误。 在重新启动机器之后,我又在12-24小时后再次失败。 此外,通知发送过程在rails服务器(nginx + unicorn)rails consoleirb上都不起作用。

我不知道从哪里开始。 即使最糟糕的是,我每天只能尝试修复一次,因为它每24小时就会破坏一次。 我认为我需要一些外部帮助来解决问题。 我错过了什么吗? 是否存在VAPID使用且需要重新启动的操作系统依赖项?


以下是一些可能有用的代码段。 很抱歉这个烂摊子,但我做了很多修改,试着让它发挥作用。

服务人员注册:

 serviceWorkerRegistration = null registerServiceWorker = -> if 'serviceWorker' of navigator && 'PushManager' of window navigator.serviceWorker.register("").then (reg) -> serviceWorkerRegistration = reg checkSubscription() .catch (error) -> console.warn 'Service Worker Error', error else console.warn 'Push messaging is not supported' registerServiceWorker() 

用户订阅和重新订阅

 # Refresh user subscription (unsub + sub) refreshUserSubscription = -> unsubscribeUser(subscribeUser) # Subscribe the user to the push server subscribeUser = -> return if !serviceWorkerRegistration # Subscribe serviceWorkerRegistration.pushManager.subscribe userVisibleOnly: true applicationServerKey: urlB64ToUint8Array('...') .then (subscription) -> pushSubscription subscription .catch (err) -> console.warn 'Failed to subscribe the user: ', err # Unsubscribe user unsubscribeUser = (callback)-> return unless serviceWorkerRegistration serviceWorkerRegistration.pushManager.getSubscription().then (subscription) -> if subscription subscription.unsubscribe().then(callback) else callback() .catch (error) -> console.warn 'Error unsubscribing', error # Push subscription to the web app pushSubscription = (subscription) -> data = if subscription then subscription.toJSON() else {} $.post "", subscription: data # Fetch current subscription, and push it. checkSubscription = () -> serviceWorkerRegistration.pushManager.getSubscription() .then (subscription) -> pushSubscription(subscription) # I think that this should be done with promises instead of a simple timeout. setTimeout refreshUserSubscription, 1000 

服务人员:

 self.addEventListener 'push', (event) -> title = "Example" options = { body: "Example" } notificationPromise = self.registration.showNotification(title, options) event.waitUntil(notificationPromise) 

网络推送:

 webpush = WebPush.new({ endpoint: '...', keys: { p256dh: '...', auth: '...' } }) webpush.set_vapid_details( "mailto:#{CONTACT_EMAIL}", "", "" ) webpush.send_notification("foo") 

将到期时间从24小时(默认)更改为12小时后,它现在可以正常工作:

 Webpush.payload_send( message: "Hi!", endpoint: "...", p256dh: "...", auth: "...", vapid: { subject: "...", public_key: ENV['VAPID_PUBLIC_KEY'], private_key: ENV['VAPID_PRIVATE_KEY'], exp: 12.hours } ) 

它看起来像库已经改变了,这是我修复它的方式:

 def send_push(payload, endpoint, p256dh, auth) Webpush.payload_send( message: payload, endpoint: endpoint, p256dh: p256dh, auth: auth, vapid: { public_key: '...', private_key: '...', expiration: 12 * 60 * 60 } ) end 

注意密钥是expiration而不是exp,它是以秒为单位的到期时间。