using System.Security.Claims;
using System.Security.Cryptography;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using static System.Net.Mime.MediaTypeNames;
using Microsoft.Net.Http.Headers;
using Microsoft.Extensions.Configuration;
using NPOI.SS.Formula.Functions;
using Serilog;
namespace OASystem.API.OAMethodLib.APNs
{
public enum NotificationType : int
{
Alert = 0,
Sound = 1,
Badge = 2,
Silent = 3
}
///
/// APNs 生成 JWT token,添加服务的时候,使用单利
///
public class APNsService : IAPNsService
{
static string token = null;
static string baseUrl = null;
private readonly IConfiguration _configuration;
private readonly IHttpClientFactory _httpClientFactory;
private readonly Serilog.ILogger _logger;
public APNsService(IConfiguration configuration, Serilog.ILogger logger, IHttpClientFactory httpClientFactory)
{
this._configuration = configuration;
this._httpClientFactory = httpClientFactory;
this._logger = logger;
//APNsService.baseUrl = this._configuration["apple:pushNotificationServer"];
APNsService.baseUrl = this._configuration["apple:pushNotificationServer_Production"];
}
///
/// 生成 APNs JWT token
///
///
public string GetnerateAPNsJWTToken()
{
return this.GetnerateAPNsJWTToken(APNsService.token);
}
///
/// 生成 APNs JWT token
///
///
private string GetnerateAPNsJWTToken(string oldToken)
{
var tokenHandler = new JwtSecurityTokenHandler();
var iat = ((DateTime.UtcNow.Ticks - new DateTime(1970, 1, 1).Ticks) / TimeSpan.TicksPerSecond);
// 判断原 token 是否超过 50 分钟,如果未超过,直接返回
if (string.IsNullOrWhiteSpace(oldToken) == false)
{
JwtPayload oldPayload = tokenHandler.ReadJwtToken(oldToken).Payload;
var oldIat = oldPayload.Claims.FirstOrDefault(c => c.Type == "iat");
if (oldIat != null)
{
if (long.TryParse(oldIat.Value, out long oldIatValue) == true)
{
// 两次间隔小于 50 分钟,使用原 token
if ((iat - oldIatValue) < (50 * 60))
{
return oldToken;
}
}
}
}
var kid = _configuration["apple:kid"];
var securityKey = _configuration["apple:securityKey"].Replace("\n", "");
var iss = _configuration["apple:iss"];
var claims = new Claim[]
{
new Claim("iss", iss),
new Claim("iat", iat.ToString())
};
var eCDsa = ECDsa.Create();
eCDsa.ImportPkcs8PrivateKey(Convert.FromBase64String(securityKey), out _);
var key = new ECDsaSecurityKey(eCDsa);
key.KeyId = kid;
var signingCredentials = new SigningCredentials(key, SecurityAlgorithms.EcdsaSha256);
var jwtHeader = new JwtHeader(signingCredentials);
var jwtPayload = new JwtPayload(claims);
var jwtSecurityToken = new JwtSecurityToken(jwtHeader, jwtPayload);
APNsService.token = tokenHandler.WriteToken(jwtSecurityToken);
return APNsService.token;
}
///
/// 发送推送通知
///
/// APP Id
/// 设备标识
/// 通知类型
/// 标题
/// 子标题
/// 通知内容
///
public async Task PushNotification(string apnsTopic, string deviceToken, NotificationType type, string title, string subtitle, string body)
{
Result result = new Result() { Code = -1, Msg = "未知错误" };
var responseData = FailedAPNsReponseData();
var token = this.GetnerateAPNsJWTToken();
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, APNsService.baseUrl + deviceToken)
{
Headers =
{
{ HeaderNames.Authorization, "bearer " + token },
{ "apns-topic", apnsTopic },
{ "apns-expiration", "0" }
},
Version = new Version(2, 0)
};
var notContent = new
{
aps = new
{
alert = new
{
title = title,
subtitle = subtitle,
body = body
}
}
};
//var content = new StringContent(JsonSerializerTool.SerializeDefault(notContent), System.Text.Encoding.UTF8, Application.Json);
var content = new StringContent(System.Text.Json.JsonSerializer.Serialize(notContent));
httpRequestMessage.Content = content;
var httpClient = _httpClientFactory.CreateClient();
try
{
var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage);
if (httpResponseMessage.IsSuccessStatusCode)
{
responseData.Code = 200;
result.Code = 0;
result.Msg = "";
result.Data = responseData;
return result;
}
else
{
responseData.Data = httpResponseMessage.StatusCode;
result.Code = -2;
result.Msg = "";
result.Data = responseData;
return result;
}
}
catch (Exception e)
{
responseData.Data = e.Message;
result.Code = -3;
result.Msg = "";
result.Data = responseData;
_logger.Information(string.Format(@" APNs : {0}", e.Message));
return result;
}
}
public APNsReponseData FailedAPNsReponseData()
{
return new APNsReponseData() { Code = 400, Data = "" };
}
}
public class APNsReponseData
{
public int Code { get; set; } = 0;
public object Data { get; set; } = "";
}
}