token-based authentication APNS推播範例使用C#
主題: |
token-based authentication APNS推播範例使用C# |
文章簡介: |
Apple於WWDC 2016推出一個關於推播服務的新功能,以token進行身份驗證並且通過HTTP/2發送推播,優點是不再需要一個APP一個憑証,也不需要擔心憑証效期的問題。本篇文章主要說明如何申請加密私鑰並且使用C#以token-based authentication方式發送推播。 |
作者: |
廖汶彬 |
版本/產出日期: |
V1.0/2016/11/21 |
1. 前言
Apple於WWDC 2016推出一個關於推播服務的新功能,以token進行身份驗證並且通過HTTP/2發送推播。原來的推播方式需要透過推播憑証來進行驗證,正常來說憑証都會有有效期,當憑証過期之後我們需要去做一個更新的動作而且通常一個APP就需要一個憑証還分為開發與正版本,但如果使用token進行身份驗証進行推播就再也不需要擔心推播憑証過期的問題也不會被一大堆推播憑証搞的頭昏腦脹。
Apple官方說明文件: Use Authentication Tokens Communicating with APNs
接下來直接說明如何申請APNs Auth Key並在C#的主控台(Console)應用程式產生JWT再透過HTTP/2發送推播通知。
2. 開發工具
Visual Studio 2015(.Net framework 4.6.2)
由於加密演算法的需要
電腦必需安裝.Net framework 4.6.2以上版本
3. 使用套件
4. 如何申請APNs Auth Key。
登入Apple Developer進入Certificates, Identifiers & Profiles
新增iOS Certificates
選取Production分類的Apple Push Notification Authentication Key (Sandbox & Production)按下確定建立APNs Auth Key
下載APNs Auth Key檔案(.p8)
5. 利用APNs Auth Key產生Json web token。
產生Json web token資料格式
Header
{
"alg": "ES256",
"kid": "ABC123DEFG"
}
Claim
{
"iss": "DEF123GHIJ",
"iat": 1437179036
}
說明:
• alg (Algorithm): 使用的加密演算法(ES256).
• kid (Key ID): 前一步驟產生之APNs Auth Key的Key ID.
• iss (Issuer): 開發者帳號的Team ID.
• iat (Issued At): 產生Token的時間(UTC)並且以秒為單位.
利用jose-jwt (2.0.2) 套件產生Json web token詳細程式碼
//讀取.p8檔案取得加密私鑰
var privateKeyContent = System.IO.File.ReadAllText(authKeyPath);
var privateKey = privateKeyContent.Split('\n')[1];
//將私鑰轉換為CngKey物件,供後續產生JWT使用
var secretKeyFile = Convert.FromBase64String(privateKey);
var secretKey = CngKey.Import(secretKeyFile, CngKeyBlobFormat.Pkcs8PrivateBlob);
//取得產生Token的時間(UTC)秒為單位
var expiration = DateTime.Now.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
var expirationSeconds = (long)expiration.TotalSeconds;
//產生JWT的資料物件
var payload = new Dictionary<string, object>()
{
{ "iss", teamId },
{ "iat", expirationSeconds }
};
var header = new Dictionary<string, object>()
{
{ "alg", algorithm },
{ "kid", apnsKeyId }
};
//利用jose-jwt套件產生Json web token
string accessToken = Jose.JWT.Encode(payload, secretKey, JwsAlgorithm.ES256, header);
6. 使用HttpClient以HTTP/2發送推播。
請注意推播服務一樣有區分為開發與正式環境
Development server : api.development.push.apple.com:443
Production server : api.push.apple.com:443
建立HttpClient利用 HttpTwo(0.0.1.19) 套件透過Http/2發送要求
設定request headers與推播payload資料
請參考 官方說明文件 中的Table 8-2 APNs request headers
詳細程式碼
//Development server:api.development.push.apple.com:443
//Production server:api.push.apple.com:443
string host = "api.development.push.apple.com";
int port = 443;
// Uri to request
var uri = new Uri(string.Format("https://{0}:{1}/3/device/{2}", host, port, registrationId));
//建立推播資料
var payloadData = JObject.FromObject(new
{
aps = new
{
alert = "Notification Message.",
badge = 2,
sound = "ping.aiff"
}
});
//UTF8編碼避免中文無法正常顯示
byte[] data = System.Text.Encoding.UTF8.GetBytes(payloadData.ToString());
//建立HttpClient物件使用HTTP/2
var handler = new Http2MessageHandler();
var httpClient = new HttpClient(handler);
//建立HttpRequestMessage,依據官方要求填入對應的資訊
//包含token、APP的bundle Id等訊息
var requestMessage = new HttpRequestMessage();
requestMessage.RequestUri = uri;
requestMessage.Headers.Add("authorization", string.Format("bearer {0}", accessToken));
requestMessage.Headers.Add("apns-id", Guid.NewGuid().ToString());
requestMessage.Headers.Add("apns-expiration", "0");
requestMessage.Headers.Add("apns-priority", "10");
requestMessage.Headers.Add("apns-topic", bundleId);
requestMessage.Method = HttpMethod.Post;
requestMessage.Content = new ByteArrayContent(data);
try
{
//發送推播
var responseMessage = await httpClient.SendAsync(requestMessage);
//取得發送結果顯示對應訊息
if (responseMessage.StatusCode == System.Net.HttpStatusCode.OK)
{
string responseUuid = string.Empty;
IEnumerable<string> values;
if (responseMessage.Headers.TryGetValues("apns-id", out values))
{
responseUuid = values.First();
}
Console.WriteLine(string.Format("\n\r*******Send Success [{0}]", responseUuid));
}
else
{
var body = await responseMessage.Content.ReadAsStringAsync();
var json = new JObject();
json = JObject.Parse(body);
var reasonStr = json.Value<string>("reason");
Console.WriteLine("\n\r*******Failure reason => " + reasonStr);
}
}
catch (Exception ex)
{
Console.WriteLine("\n\r*******Exception message => " + ex.Message);
}
7. 程式碼(GitHub)。
https://github.com/MonkeyBinBin/TokenbasedAPNsSample
專案不含私鑰檔案與詳細設定
請參考程式中的說明與官方文件