DownOL 软件仓库– 软件下载,字节世界与新知

iOS开发多App推送还申请多推送证书?我只能说你out了!

发表于:2024-05-03 作者:下载之友
编辑最后更新 2024年05月03日,新的APNs协议基于HTTP/2,一种是使用Universal Push Notification Client SSL 证书,一种是使用Token,本文主要讲基于token的APNs协议。基于HTT

新的APNs协议基于HTTP/2,一种是使用Universal Push Notification Client SSL 证书,一种是使用Token,本文主要讲基于token的APNs协议。

基于HTTP/2与Token的 APNs 协议

APNs Provider(即,APP的后台) API 允许您向您的 iOS,macOS 设备上的应用程序和 Apple Watch 发送远程通知。API 基于 HTTP/2 网络协议。每个交互通过一个 POST 请求,包含 JSON 的有效Payload负载,通过服务器使用Auth Key生成服务端token连接APNs服务器,并且通过设备token发送负载。APNs然后转发给特定设备的指定应用程序。

  • Request 和 Response 使用JSON通信

  • APNs支持状态码和返回 error 信息

  • APNs推送成功时 Response 将返回状态码200

  • APNs推送失败时,Response 将返回 JSON 格式的 Error 信息。

  • 最大推送长度提升到4096字节(4Kb)

  • 可以通过 "HTTP/2 PING " 心跳包功能检测当前 APNs 连接是否可用,并能维持当前长连接。

  • 支持为不同的APP定义 "topic"(其实就是App Bundle ID)

  • 多个推送App,只需要一个Apple Push APNs Auth Key

Apple Push Notification Authentication Key

2016年9月,苹果悄悄上线了token验证的推送方式,通过获得一个认证密钥(APNs Auth Key)去生成服务器端token,并且token非常容易生成,可以使用这些token令牌代替推送证书。一个认证密钥可用于多个应用程序并且永远不过期。每一个需要推送的App都需要配置推送证书的时代过去了。but,大部分第三方推送服务商,目前都没有升级到APNs Auth Key Token模式。

Auth key 生成

开发者网站证书页面:https://developer.apple.com/account/ios/certificate/key选择Apple Push Notification Authentication Key (Sandbox & Production)并且点击页面底部的Continue

网站会生成包含APNs Auth Key的.p8密钥文件,下载下来APNsAuthKey_xxxxxxx.p8后续连接APNS使用。

协议说明

这种方式适合在基于HTTP/2协议的Provider使用,它与APNs之间的连接通过JWT(JSON web tokens)来验证。在这种方式下不需要使用证书+私钥的方式来建立可靠连接。Provider只需要提供一对公私钥(私钥给APNs保存,公钥Provider自己保存),并使用其中的私钥生成并加密JWT Token,每次向APNs请求推送的时候带上这个Token即可。token必须定期更新,每一个APNs provider验证token有效期为一个小时。具体步骤如下:

  1. Provider通过TLS向APNs发起请求。

  2. APNs返回一个证书给Provider。

  3. Provier验证这个证书。通过后,发送push数据并带上JWT token。

  4. APNs验证token,并返回请求的结果。

重要:建立TLS连接必须要有一个GeoTrust Global CA root certificate,在macOS中,这个证书已经安装在keychain中,如果是其他操作系统则可以在GeoTrust Root Certificates website下载。

HTTP/2请求APNs

HTTP/2 请求字段

字段名字段值
:methodPOST
:path/3/device/

对于 参数,指定目标设备的十六进制字节。APNs需要使用 HPACK (HTTP/2 的报头压缩),防止重复的标题键和值。APNs为HPACK维护一个小的动态表。为了避免填满了APNs HPACK表,因而必须丢弃表数据,编码headers以下列方式 - - 尤其是当发送大量的流︰:method 必须为POST:path 值应编码为一个header中的一个文本字段,没有索引authorization 请求header,如果存在,应编码为一个header中的一个文本字段,没有索引apns-id、 apns-expiration,和 apns-collapse-id 不同的编码取决于它是否是第一次初始请求或随后的 POST 操作的一部分,如下︰第一次发送请求头的时,使增量索引编码允许头名称加入动态表中子请求发送头时,编码成header中的一个文本字段,没有索引

请求头

APNs 请求头 http headers

Header 头描述
authorization提供的发送到指定(app)主题通知的APNs验证token,token是以Base64 URL编码的JWT格式。指定为bearer 当使用证书连接的时候,这个请求头将会被忽略
apns-id一个规范的的 UUID 用来标识通知。如果发送通知时发生错误,APNs 使用此值来标识通知,通知到您的服务器。规范的格式是 32 个小写的十六进制数字,由连字元分隔为5,8-4-4-4-12。UUID一个例子如下:123e4567-e89b-12d3-a456-42665544000如果您省略这个头,一个新的UUID由APNs创建并且在response中返回
apns-expiration通知过期时间,秒级的UTC时间戳,这个header标识通知 从何时起不再有效,可以丢弃。如果这个值非零,APNs保存通知并且尽量至少送达一次。如果无法第一时间送达,根据需要重复尝试。如果这个值为0,APNs认为通知立即过期,不会存储与重新推送。
apns-priority通知的优先级,指定以下值:
  • 10-立即推送消息. 这个优先级的通知必须再目标设备上触发alert, 声音, 或者badge . 仅仅包含content-available key使用此优先级是错误的。
  • 5-一次性发送通知,需要考虑设备的电量。具有此优先级的通知可能分组推送并且几种爆发推送 Notifications with this priority might be grouped and delivered他们会被节流,并且在某些情况下不会送达。
如果省略这个头,APNs服务器会把优先级设置为10
apns-topic远程通知的主题,通常是你App的bundle id,在开发者账号中创建的证书必须包含此bundle id如果证书包含多个主题,这个头必须指定一个值如果省略此头并且APNs证书不包含指定的主题,APNs服务器使用证书的Subject作为默认主题。如果使用token代替证书,必须指定此头,提供的主题应该被在开发者账号中的team提供。即bundle id的app应该与auth key同属于一个开发者组。
apns-collapse-id具有相同的折叠标识符的多个通知推送给用户合并显示为单个通知。比如: apns-collapse-id : 2 ,那么value为2的消息将被APNS合并成一条消息推送给设备。此关键字的值不能超过 64 个字节。更多的信息,请参阅Quality of Service, Store-and-Forward, and Coalesced Notifications。

Provider Authentication Tokens

关于JWT(JSON Web Token)的详细资料可以通过这里了解。同时也可以从这里找到一些现成可用的库。下面对JWT进行详细的介绍,一个JWT实际上是一个JSON对象,它的头部必须包含:

  • 用以加密token的加密算法(alg) ,比如:ES256。

  • 10个字元长度的标识符(kid),(苹果开发者网站创建的APNs Auth Key详情中的key id)

同时他的claims payload部分必须包含:

  • issuer(iss) registered claim key,其值就是10个字元长的Team ID。

  • issued at (iat) registered claim key,其值是一个秒级的UTC时间戳。

比如:

{

创建完这个token后,必须使用自己的私钥对其进行加密,然后再采用基于P-256曲线和SHA-256哈希算法的椭圆曲线数字签名算法(ECDSA)进行签名,并将alg键的值设置为ES256。(注意:APNs只支持ES256签名的JWT,否则会返回InvalidProviderToken(403)错误)为了保证安全,APNs要求定期更新token,时间间隔为1小时,如果APNs发现当前的时间戳与iat值中的时间戳相比,大于一个小时,那么APNs会拒绝推送消息,并返回ExpiredProviderToken (403)错误。

请求体

body内容是将要推送的消息负载的JSON对象。body数据不必须压缩,其最大大小为4 KB(4096字节)。对于(VoIP)通知,body数据最大大小为5 KB(5120字节)。

{ "aps" : { "alert" : "Hello HTTP/2" } }

有关负载中的具体的键和值,请参阅Payload Key Reference。

响应头

APNs 响应头 response headers

Header 名Header值
apns-idapns-id从request得到, 如果request中没有此值, APNs服务器创建一个新的UUID并且在header中返回
:statusHTTP状态码,HTTP status code.可能status codes, 参加下文

响应状态码

Status codes for an APNs response

Status codeDescription
200成功
400无效请求
403证书错误或者验证token错误
405:method 设置错误. 只支持 POST 请求
410device token不在有效与topic
413负载payload太大
429服务端对于同一个device token发送了太多了请求
500内部服务器错误
503服务器关闭,不可用

使用

Sample request for a provider authentication token

HEADERS

- END_STREAM

+ END_HEADERS

:method = POST

:scheme = https

:path = /3/device/00fc13adff785122b4ad28809a3420982341241421348097878e577c991de8f0

host = api.development.push.apple.com

authorization = bearer eyAia2lkIjogIjhZTDNHM1JSWDciIH0.eyAiaXNzIjogIkM4Nk5WOUpYM0QiLCAiaWF0I

jogIjE0NTkxNDM1ODA2NTAiIH0.MEYCIQDzqyahmH1rz1s-LFNkylXEa2lZ_aOCX4daxxTZkVEGzwIhALvkClnx5m5eAT6

Lxw7LZtEQcH6JENhJTMArwLf3sXwi

apns-id = eabeae54-14a8-11e5-b60b-1697f925ec7b

apns-expiration = 0

apns-priority = 10

apns-topic =

DATA

+ END_STREAM

{ "aps" : { "alert" : "Hello" } }

APNs Auth Key 与 shell

#!/bin/bash

# Get curl with HTTP/2 and openssl with ECDSA: 'brew install curl openssl'

curl=/usr/local/opt/curl/bin/curl

openssl=/usr/local/opt/openssl/bin/openssl

# --------------------------------------------------------------------------

# HostDevelopment = "https://api.development.push.apple.com"

# HostProduction = "https://api.push.apple.com"

deviceToken=b27371497b85611baf9052b4ccfb9641ab7fea1d01c91732149c99cc3ed9342f

authKey="./APNSAuthKey_ABC1234DEF.p8"

authKeyId=ABC1234DEF

teamId=TEAM123456

bundleId=com.example.myapp

endpoint=https://api.development.push.apple.com

read -r -d '' payload <<-'EOF'

{

"aps": {

"badge": 2,

"category": "mycategory",

"alert": {

"title": "my title",

"subtitle": "my subtitle",

"body": "my body text message"

}

},

"custom": {

"mykey": "myvalue"

}

}

EOF

# --------------------------------------------------------------------------

base64() {

$openssl base64 -e -A | tr -- '+/' '-_' | tr -d =

}

sign() {

printf "$1"| $openssl dgst -binary -sha256 -sign "$authKey" | base64

}

time=$(date +%s)

header=$(printf '{ "alg": "ES256", "kid": "%s" }' "$authKeyId" | base64)

claims=$(printf '{ "iss": "%s", "iat": %d }' "$teamId" "$time" | base64)

jwt="$header.$claims.$(sign $header.$claims)"

$curl --verbose \

--header "content-type: application/json" \

--header "authorization: bearer $jwt" \

--header "apns-topic: $bundleId" \

--data "$payload" \

$endpoint/3/device/$deviceToken

后续更新其他语言推送方法。

参考资料

CommunicatingwithAPNs

http://thrysoee.dk/apns/

2022-05-09 23:39:23
0