iOS 開發之新版 APNs 搭建必備知識
一、設備token和消息的生命周期
關于設備token以及推送消息的生命周期需要注意下面幾點:
- Token會在iOS系統更新或者設備數據、設置被擦除的時候改變。
- 當設備離線的時候,APNS會將消息數據存儲一段時間,等設備上線后重發。如果設備在離線期間,向APNS發送了多條推送消息,APNS將會丟棄掉前面的一些消息,只保留后面的消息,要是設備長時間離線,則會將所有的消息丟棄掉。
- 可以通過設置http/2頭中的 apns-collapse-id 鍵值對來合并消息,比如: apns-collapse-id : 2 ,那么value為2的消息將被APNS合并成一條消息推送給設備。
二、Provider(后臺)與APNs的交互
Provider(即,APP的后臺)與APNS有兩種安全的交互方式,都必須采用TLS以保證可靠性,更詳細的內容繼續往下看。
TLS 的簡單理解是,為了保證數據傳輸安全,在HTTP層與TCP層之間插入的一個安全校驗層,它所做的事情簡單來說就是: “通過CA申請的證書驗證client與server是可靠的之后,通過相應的公私鑰加密一個 協商的公鑰 ,之后的真實數據傳輸就使用這個公鑰進行加密,以保證數據的安全可靠性” ,關于TLS的更詳細的介紹
基于Token的方式(Token-Based Provider-to-APNs Trust)
1、流程概述
這種方式適合在基于HTTP/2協議的Provider使用,它與APNs之間的連接通過 JWT(JSON web tokens) 來驗證。在這種方式下不需要使用證書+私鑰的方式來建立可靠連接。Provider只需要提供一對公私鑰(私鑰給APNs保存,公鑰Provider自己保存),并使用其中的私鑰生成并加密JWT Token,每次向APNs請求推送的時候帶上這個Token即可。
具體步驟如下:
- Provider通過 TLS 向APNs發起請求。
- APNs返回一個證書給Provider。
- Provier驗證這個證書。通過后,發送push數據并帶上JWT token。
- APNs驗證token,并返回請求的結果。
Establishing and using token-based connection trust between a provider and APNs
建立TLS連接必須要有一個 GeoTrust Global CA root certificate ,在macOS中,這個證書已經安裝在keychain中,如果是其他操作系統則可以在 GeoTrust Root Certificates website 下載。
2、Provider Authentication Tokens
關于JWT(JSON Web Token)的詳細資料可以通過 這里 了解。同時也可以從 這里 找到一些現成可用的庫。
下面對JWT進行詳細的介紹,一個JWT實際上是一個JSON對象,它的頭部必須包含:
- 用以加密token的加密算法( alg ) ,比如:ES256。
- 10個字符長度的標識符( kid ),(登入蘋果開發者賬號后,進入到 Certificates, Identifiers & Profiles ,然后點擊 APNs Auth Key ,最后在右側找到 Apple Push Notification Authentication Key (Sandbox & Production) 選項,點擊創建后可以創建一個p8文件。)
同時他的claims payload部分必須包含:
- issuer( iss ) registered claim key,其值就是10個字符長的Team ID。
- issued at ( iat ) registered claim key,其值是一個秒級的UTC時間戳。
比如:
{
"alg": "ES256",
"kid": "ABC123DEFG"
}
{
"iss": "DEF123GHIJ",
"iat": 1437179036
}
創建完這個token后,必須使用自己的私鑰對其進行加密,然后再采用基于P-256曲線和SHA-256哈希算法的橢圓曲線數字簽名算法(ECDSA)進行簽名,并將 alg 鍵的值設置為 ES256 。(注意:APNs只支持 ES256 簽名的JWT,否則會返回 InvalidProviderToken(403) 錯誤)
為了保證安全,APNs要求定期更新token,時間間隔為1小時,如果APNs發現當前的時間戳與 iat 值中的時間戳相比,大于一個小時,那么APNs會拒絕推送消息,并返回 ExpiredProviderToken (403) 錯誤。
基于證書的方式(Certificate-based connection trust)
流程概述
這種方式是指Provider可以采用一個唯一的證書以及一個加密的私鑰來與APNs交互,其中證書是由蘋果產生的(通過蘋果賬號登錄到 developer account 配置創建)。整個交互過程如下:
- Provider通過 TLS 向APNs請求連接。
- APNs向Provider返回一個APNs證書。
- Provider驗證這個APNs證書,并將從蘋果官網獲取的證書返回給APNs。
- APNs驗證通過后,這個鏈接就算是建立了。
Establishing certificate-based connection trust between a provider and APNs
APNs Provider Certificates
創建步驟可以參考 Configure push notifications 中的Generate a universal APNs client SSL certificate章節。
三、APNs連接
連接的管理
蘋果的兩個APNs server分別為:
- Development server: api.development.push.apple.com:443
- Production server: api.push.apple.com:443
要與APNs交互要求server必須支持1.2及上版本的TLS協議。通過上面的介紹我們已經知道,server跟APNs交互有兩種方式:基于JWT的方式以及基于證書的方式。為了保證高質量的使用APNs應該注意如下幾點:
- 對于基于JWT的方式,應該定時更新token,token的有效期為1小時。
- 對于基于JWT的方式,能每次請求都創建新的token,盡量在一小時內使用同一個token。
- 不能頻繁的建立、關閉連接,否則APNs會把這當做是黑客攻擊,拒絕訪問。應該盡量將連接保活,直到你認為這個連接接下來會長時間不使用為止。
- 當需要發送大量的推送數據的時候,可以同時創建多個連接,以改善性能。
- 當吊銷證書或者token時,應該關閉所有相關的連接。
HTTP/2的請求與響應
詳情可參見蘋果官方文檔 HTTP/2 Request to APNs 。這里介紹了接口的 請求參數 、 返回結果 , 錯誤碼 以及 示例代碼 。下面僅截取了其中的例子,以加深對APNs的使用的理解:
-
基于證書的方式的request:
HEADERS
- END_STREAM + END_HEADERS :method = POST :scheme = https :path = /3/device/00fc13adff785122b4ad28809a3420982341241421348097878e577c991de8f0 host = api.development.push.apple.com apns-id = eabeae54-14a8-11e5-b60b-1697f925ec7b //可以不填,如果不填APNs會自己創建一個UUID并在response中返回 apns-expiration = 0 apns-priority = 10
DATA
+ END_STREAM { "aps" : { "alert" : "Hello" } }</code></pre> </li>
基于token方式的request:
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 = <MyAppTopic> DATA
+ END_STREAM { "aps" : { "alert" : "Hello" } }</code></pre> </li>
失敗后的response:
HEADERS
- END_STREAM + END_HEADERS :status = 400 content-type = application/json apns-id: <a_UUID>
DATA
+ END_STREAM { "reason" : "BadDeviceToken" }</code></pre> </li>
成功后的response:
HEADERS
- END_STREAM + END_HEADERS apns-id = eabeae54-14a8-11e5-b60b-1697f925ec7b :status = 200</code></pre> </li>
</ul>
四、設備token的生成與分發
在app啟動的時候,必須向iOS系統注冊遠程推送,成功后,蘋果將會返回一個設備token給app,此時app就可以將這個token上報給自己的后臺。
如果有必要產生一個新的token,APNs會使用設備證書生成一個token(其中包含了一個設備ID),并使用token key加密后返回給設備。同時設備會將這個token以 NSData 對象的形式返回給app,app獲取到該token之后應該將其發送到自己后臺,后臺之后就可以通過這個token來發送推送數據。過程如下圖:
Managing the device token
最后
通過蘋果的官方文檔我們可以知道provider與APNs的交互過程中,需要注意一下幾點:
- 推薦使用 HTTP/2 協議。
- 必須加入 TLS 層。
- 基于JWT的方式,token的最大有效期為1小時,并且不能頻繁更換token。
- 不能頻繁創建、關閉連接,應該盡量少開連接,如果過于頻繁,APNs將把其當做是黑客攻擊,但是如果數據量大,可以同時多個連接向APNs發送消息。
- 吊銷token或者證書的時候,應該及時關閉老的連接。
參考文獻
來自:http://www.jianshu.com/p/d8dba6c2c07a