| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391 | 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 System.Net.Http;using Flurl.Http.Configuration;using System.Net;using QuzrtzJob.Factory;using Org.BouncyCastle.Crypto.Parameters;using System.IO;using CurlThin;using CurlThin.Enums;using CurlThin.Helpers;using CurlThin.Native;using CurlThin.SafeHandles;namespace OASystem.API.OAMethodLib.APNs{    public enum NotificationType : int    {        Alert = 0,        Sound = 1,        Badge = 2,        Silent = 3    }    /// <summary>    /// APNs 生成 JWT token,添加服务的时候,使用单利    /// </summary>    public class APNsService : IAPNsService    {        static string token = string.Empty;        static string baseUrl = string.Empty;        private readonly ILogger<APNsService> _logger;        //private static readonly HttpClient _httpClientFactory = new HttpClient { BaseAddress = new Uri("https://api.push.apple.com:443/3/device/") };        private readonly IConfiguration _configuration;        //private readonly IHttpClientFactory _httpClientFactory;        public APNsService(ILogger<APNsService> logger, IConfiguration configuration)        {            this._configuration = configuration;            //this._httpClientFactory = httpClientFactory;            //APNsService.baseUrl = this._configuration["apple:pushNotificationServer"];            //APNsService.baseUrl = this._configuration["apple:pushNotificationServer_Production"];            //            //APNsService.baseUrl = string.Format("https://api.push.apple.com:443/3/device/");            _logger = logger;        }        /// <summary>        /// 生成 APNs JWT token        /// </summary>        /// <returns></returns>        public string GetnerateAPNsJWTToken()        {            return this.GetnerateAPNsJWTToken(APNsService.token);        }        //private static CngKey GetPrivateKey()        //{        //    using (var reader = System.IO.File.OpenText("File/AuthKey_RMV7Y4KM9V.p8"))        //    {        //        var ecPrivateKeyParameters = (ECPrivateKeyParameters)new PemReader(reader).ReadObject();        //        var x = ecPrivateKeyParameters.Parameters.G.AffineXCoord.GetEncoded();        //        var y = ecPrivateKeyParameters.Parameters.G.AffineYCoord.GetEncoded();        //        var d = ecPrivateKeyParameters.D.ToByteArrayUnsigned();        //        return EccKey.New(x, y, d);        //    }        //}        /// <summary>        /// 生成 APNs JWT token        /// </summary>        /// <returns></returns>        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 securityKey = string.Format("MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQglyUl7hjI75YJUVMbZLN6TpkiFzuTXUN+UIjuJA7+y8ugCgYIKoZIzj0DAQehRANCAAS8GR7lKNst4KENCp45OXCMyiytvzK0qdRBrx0l+bMaHjiU+Upfox82G+Xy4wd8hI+0wMDh341aNelqEdYUUx3O");            var iss = _configuration["apple:iss"];            var claims = new Claim[]            {                new Claim("iss", iss),                new Claim("iat", iat.ToString())            };            string msg = string.Empty;            msg += $"[GetnerateAPNsJWTToken] 0";            try            {                msg += $"[GetnerateAPNsJWTToken] 1";                var eCDsa = ECDsa.Create();                msg += $"[GetnerateAPNsJWTToken] 2";                eCDsa.ImportPkcs8PrivateKey(Convert.FromBase64String(securityKey), out _);                msg += $"[GetnerateAPNsJWTToken] 3";                var key = new ECDsaSecurityKey(eCDsa);                msg += $"[GetnerateAPNsJWTToken] 4";                key.KeyId = kid;                msg += $"[GetnerateAPNsJWTToken] 5";                var signingCredentials = new SigningCredentials(key, SecurityAlgorithms.EcdsaSha256);                msg += $"[GetnerateAPNsJWTToken] 6";                var jwtHeader = new JwtHeader(signingCredentials);                msg += $"[GetnerateAPNsJWTToken] 7";                var jwtPayload = new JwtPayload(claims);                msg += $"[GetnerateAPNsJWTToken] 8";                var jwtSecurityToken = new JwtSecurityToken(jwtHeader, jwtPayload);                msg += $"[GetnerateAPNsJWTToken] 9";                APNsService.token = tokenHandler.WriteToken(jwtSecurityToken);            }            catch (Exception ex)            {                return msg += $"[GetnerateAPNsJWTToken] --> ExMsg:[{ex.Message}]";            }            return APNsService.token;        }        public async Task<Result> PushNotification1(string apnsTopic, string deviceToken, NotificationType type, string title, string subtitle, string body, bool isTarget, string viewCode, PageParam_PriceAuditH5 pageParam)        {            var res = new Result() { Code = 0, Msg = "", Data = "" };            //This string is for extracting libcurl and ssl libs to the bin directory.            CurlResources.Init();            var global = CurlNative.Init();            var easy = CurlNative.Easy.Init();            string content = string.Empty;            try            {                var token = GetnerateAPNsJWTToken();                var dataCopier = new DataCallbackCopier();                CurlNative.Easy.SetOpt(easy, CURLoption.URL, $"https://api.push.apple.com:443/3/device/{deviceToken}");                CurlNative.Easy.SetOpt(easy, CURLoption.WRITEFUNCTION, dataCopier.DataHandler);                //This string is needed when you call a https endpoint.                CurlNative.Easy.SetOpt(easy, CURLoption.CAINFO, CurlResources.CaBundlePath);                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)                };                if (isTarget)                {                    var notContent = new                    {                        aps = new                        {                            alert = new                            {                                title = title,                                subtitle = subtitle,                                body = body                            }                        },                        ViewCode = viewCode,                        PageParam = pageParam                    };                    var contentJson = new StringContent(System.Text.Json.JsonSerializer.Serialize(notContent), System.Text.Encoding.UTF8, Application.Json);                    CurlNative.Easy.SetOpt(easy, CURLoption.POSTFIELDS, System.Text.Json.JsonSerializer.Serialize(notContent));                }                else                {                    var notContent = new                    {                        aps = new                        {                            alert = new                            {                                title = title,                                subtitle = subtitle,                                body = body                            }                        }                    };                    var contentJson = new StringContent(System.Text.Json.JsonSerializer.Serialize(notContent), System.Text.Encoding.UTF8, Application.Json);                    CurlNative.Easy.SetOpt(easy, CURLoption.POSTFIELDS, System.Text.Json.JsonSerializer.Serialize(notContent));                }                var headers = CurlNative.Slist.Append(SafeSlistHandle.Null, $"Authorization: Bearer {token}");                CurlNative.Slist.Append(headers, $"apns-topic: {apnsTopic}");                CurlNative.Slist.Append(headers, $"apns-expiration: 0");                CurlNative.Easy.SetOpt(easy, CURLoption.HTTPHEADER, headers.DangerousGetHandle());                //Your set of ciphers, full list is here https://curl.se/docs/ssl-ciphers.html                CurlNative.Easy.SetOpt(easy, CURLoption.SSL_CIPHER_LIST, "ECDHE-RSA-AES256-GCM-SHA384");                res.Msg += $"[PushNotification1] --> 发送请求";                CurlNative.Easy.Perform(easy);                content = Encoding.UTF8.GetString(dataCopier.Stream.ToArray());            }            catch (Exception ex)            {                res.Msg += $"[PushNotification1] ExMsg:{ex.Message}";                if (ex.InnerException != null)                {                    res.Msg += $"[PushNotification1] InnerException:{ex.InnerException.Message}";                }            }            finally            {                easy.Dispose();                if (global == CURLcode.OK)                    CurlNative.Cleanup();            }            res.Data = content;            return res;        }        /// <summary>        /// 发送推送通知        /// </summary>        /// <param name="apnsTopic">APP Id</param>        /// <param name="deviceToken">设备标识</param>[文件:AuthKey_RMV7Y4KM9V.p8]        /// <param name="type">通知类型</param>        /// <param name="title">标题</param>        /// <param name="subtitle">子标题</param>        /// <param name="body">通知内容</param>        /// <returns></returns>        public async Task<Result> PushNotification(string apnsTopic, string deviceToken, NotificationType type, string title, string subtitle, string body)        {            Result result = new Result() { Code = -1, Msg = "[PushNotification] Start" };            result.Msg += "\r\n[PushNotification] Start";            _logger.LogInformation($"[PushNotification] Start");            var responseData = FailedAPNsReponseData();            var token = this.GetnerateAPNsJWTToken();            try            {                var _httpClientFactory = new HttpClient { BaseAddress = new Uri("https://api.push.apple.com:443/3/device/") };                result.Msg += $"\r\n[PushNotification] --> [HttpClient] --> Init --> jsonStr:{JsonConvert.SerializeObject(_httpClientFactory)}";                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)                };                result.Msg += $"\r\n[PushNotification] --> [httpRequestMessage] --> Init --> jsonStr:{JsonConvert.SerializeObject(httpRequestMessage)}";                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), System.Text.Encoding.UTF8, Application.Json);                httpRequestMessage.Content = content;                result.Msg += $"\r\n[PushNotification] --> [httpRequestMessage] --> Content --> jsonStr:{JsonConvert.SerializeObject(httpRequestMessage)}";                try                {                    //ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls13;                    //HttpClientHandler handler = new HttpClientHandler()                    //{                    //    Proxy = new WebProxy("https://api.push.apple.com:443/3/device/"),                    //    SslProtocols = System.Security.Authentication.SslProtocols.Tls12, // Enforce TLS 1.2                    //    UseProxy = true                    //};                    //HttpClient client = new HttpClient(handler);                    //var httpResponseMessage = await client.SendAsync(httpRequestMessage);                    var httpResponseMessage = await _httpClientFactory.SendAsync(httpRequestMessage);                    if (httpResponseMessage.IsSuccessStatusCode)                    {                        responseData.Code = 200;                        result.Code = 0;                        result.Msg = "";                        result.Data = responseData;                        result.Msg += $"\r\n[PushNotification] End jsonStr[{JsonConvert.SerializeObject(result)}]";                        return result;                    }                    else                    {                        responseData.Data = httpResponseMessage.StatusCode;                        result.Code = -2;                        result.Msg = "";                        result.Data = responseData;                        result.Msg += $"\r\n[PushNotification] End jsonStr[{JsonConvert.SerializeObject(result)}]";                        return result;                    }                }                catch (Exception e)                {                    responseData.Data = e.Message;                    result.Code = -3;                    result.Msg = "";                    result.Data = responseData;                    result.Msg += $"\r\n[PushNotification] --> [httpClientFactory] ExceptionMsg:{e.Message}";                    result.Msg += $"\r\n[PushNotification] --> [httpClientFactory] InnerExceptionData:{e.InnerException}";                    result.Msg += $"\r\n[PushNotification] --> [httpClientFactory] InnerExceptionMsg:{e.InnerException.Message}";                    return result;                }            }            catch (Exception ex)            {                result.Msg += $"\r\n[PushNotification] InnerExceptionData:{JsonConvert.SerializeObject(ex.InnerException)}";                result.Msg += $"\r\n[PushNotification] InnerExceptionMsg:{ex.InnerException.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; } = "";    }    public class PageParam_PriceAuditH5    {        public string diid { get; set; }        public string uid { get; set; }        public string itemId { get; set; }    }}
 |