推送通知概览【译】

Posted by Neil Ning on 2023-07-16
翻译

前言

该文章翻译自web.dev的Notifications系列文章,本篇文章原文链接点击这里

什么是推送通知

消息推送允许你推送消息给用户,即使用户没有在浏览你的网站。这个技术之所以被称为消息推送,是因为你可以推送信息给用户,从概念上它区别于主动获取消息的技术。
通知展示一段信息给用户,网站可以使用通知技术来告诉用户一些重要的、紧急的事件,或者提示用户需要执行的动作。不同的平台,通知的形式各不相同。
notification.png

消息推送和通知其实是两个独立,但又相互补充的技术。推送技术指的是你可以从服务器向用户发送消息,即使用户并没有在使用你的网站。通知技术指的是在用户的设备上展示推送的信息。我们可以使用通知技术展示信息,不仅限于推送消息。那反过来,我们可以静默推送,而不使用通知功能来告知用户吗?答案是否定的,或许未来有一天是可以的,但是目前浏览器还不允许这么做。即目前通过消息推送技术获取的信息,必须显式的通知用户。非技术用户可能不明白消息推送和通知之间的区别。在这篇文章中我们提到推送通知(push notifications)时,我们指得是消息推送和通知两种技术,当我们提到消息推送(push messages)时,指得是推送技术本身,当我们说到通知(notifications)时,指得仅仅是通知技术。

为什么要使用推送通知?

  • 对用户来说,推送通知可以定时接收到有价值、准确的信息
  • 对网站开发者来说,推送通知时增加网站用户粘性的方式之一

推送通知时如何工作的

在一个更高的纬度来看,实现推送通知主要包含一下几个关键步骤:

  1. 在客户端实现代码逻辑来获取通知相关的用户权限。然后将授权后获得的客户端标识发送给服务器并存储在数据库中。
  2. 在服务端实现向客户端发送消息的逻辑。
  3. 在客户端添加逻辑:接收上一步推送给客户端设备的消息,并通过通知技术展示给用户。

下面将详细解释上面的几个步骤。

获取推送通知权限

首先,网站需要获得用户的授权,来推送通知。这个动作必须由用户触发,例如点击询问提示框的确认按钮,确认之后,我们需要调用Notification.requestPermission(),操作系统或者浏览器会展示某种形式的UI来正式通知询问用户是否给予授权。授权弹框的UI在各个平台上各不相同。

客户端订阅推送通知

得到用户的授权之后,网站必须在JavaScript中,利用Push API初始化订阅推送。在这个过程中,你必须提供一个公钥,后面将会详细解释公钥。发起订阅过程时,浏览器向推送服务器发送一次网络请求,这个后面也会详细解释这个过程。
以上步骤成功之后,浏览器会返回PushSubscription
对象,你必须将这个对象保存下来,通常是将这个对象发送给后端服务器并存储在数据库当中。
subscription.jpg

发送消息

你的服务器并不会直接向客户端发送消息,而是由推送服务(push service)来实现这个过程。推送服务由浏览器厂商控制。当你发送通知给客户端时,你需要向推送服务发起网络请求,这个请求被称之为web push protocol request,web push protocol request包含以下几部分:

  • 要发送的数据
  • 要发送给哪个客户端
  • 一些指令,用来指定消息的交付模式,例如指定推送服务应该在十分钟之后停止再次尝试发送消息。

通常这个请求是在网站的服务端发起的。当然,你不需要从零开始构建这个请求,已经有很多库可以帮你实现这个功能。如web-push-libs,这个过程主要是通过HTTP来实现的。
request.jpg

推送服务收到请求后,经过认证,将会把消息发送给正确的客户端。如果浏览器是离线状态,推送服务将会把消息放入队列中,直到浏览器恢复在线状态。

推送服务是由浏览器厂商指定的,网站开发者不能指定它。不过用户并不需要关心,因为所有的推送服务都必须遵守推送协议。你只需要按照协议的规范,在服务端发送正确的请求即可。根据规范,这个请求需要带上特殊的请求头,数据必须以字节流的形式发送。
你需要关注的是向推送服务发送正确的请求,订阅推送成功后,浏览器会返回给你一个PushSubscription对象,该对象包含了有关推送服务的信息:

1
2
3
4
5
6
7
8
{
"endpoint": "https://fcm.googleapis.com/fcm/send/c1KrmpTuRm…",
"expirationTime": null,
"keys": {
"p256dh": "BGyyVt9FFV…",
"auth": "R9sidzkcdf…"
}
}

endpoint字段域名指得就是推送服务的地址,endpoint字段的路径是客户端的唯一标识,它用来帮助推送服务将消息准确的发送给客户端。keys字段是用来加密信息的,这个稍后也会讲到。

推送消息加密

发送给推送服务的数据必须是经过加密的,这能保证即使是推送服务也不能查看你要发送给客户端的数据。这是因为推送服务是由浏览器厂商决定的,理论上也是不安全的。你的服务器必须使用PushSubscription提供的keys字段加密你要发送给推送服务的请求数据。

加密推送服务请求

推送服务提供一种特别的方式来阻止其他人像你的客户发送请求。严格来说你不需要关注这个,但在Chrome浏览器上是必须的,浏览器提供一种简便的方式来处理加密,在Firefox这是可选的。其他浏览器在未来可能也会支持该选项。
加密过程需要你的服务器提供唯一的公钥和私钥对。认证的大致流程如下:

  • 生成唯一的包含公钥和私钥的密钥对。该密钥对就是我们所说的应用服务密钥application server keys,它也被称为VAPID keysVAPID是一个规范,定义了这个认证的过程。
  • 当你在JavaScript代码中订阅推送通知时,你需要提供公钥,然后推送服务会为该订阅设备生成一个endpoint,该endpoint和你提供的公钥的建立关联关系。
  • 当你在服务端向推送服务发送请求时,需要使用私钥对你发送的JSON数据进行签名加密。
  • 当推送服务收到该请求时,会使用已存储的公钥对加密的数据进行认证。如果认证成功,推送服务就能知道这个请求来自之前已经订阅的某个网站用户。

自定义推送消息

web推送协议规范定义了一些参数,允许你指定消息的推送方式,例如:

  • 消息的生命周期(Time-To-Live,TTL)定义了推送服务分发消息的时长。
  • 消息的紧急程度,这对向发送更高优先级的消息很有用。
  • 消息的主题,他会替代还未正式发送的,有相同主题的消息。

以通知的形式展示推送消息

一旦你向推送服务发送请求,推送服务会将你的消息放入队列,直到发生以下事件发生:

  1. 客户端上线状态,推送服务将消息分发给客户端
  2. 消息过期

当客户端浏览器收到推送的消息,会对推送的消息数据进行解密,并触发Service Worker的push事件,service worker是运行在后台的JavaScript代码,即使用户关闭了你的网站,甚至关闭了浏览器。在service worker push事件的回调函数中,你需要调用ServiceWorkerRegistration.showNotification()以通知的形式展示收到的消息。
event.jpg