使用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 console和irb上都不起作用。
我不知道从哪里开始。 即使最糟糕的是,我每天只能尝试修复一次,因为它每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,它是以秒为单位的到期时间。