using Furion.FriendlyException; using Furion.HttpRemote; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Options; using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Security.Cryptography; using System.Text; using System.Threading; using System.Threading.Tasks; namespace FlexJobApi.Core { public class AliyunSmsUtils { private readonly IOptions options; private readonly IHttpRemoteService httpRemoteService; public AliyunSmsUtils( IOptions options, IHttpRemoteService httpRemoteService) { this.options = options; this.httpRemoteService = httpRemoteService; } /// /// 发送短信 /// /// 手机号码 /// 模板代码 /// 模板参数 /// 取消令牌 /// /// public async Task SendAsync(string phoneNumber, EnumSmsTemplateCode templateCode, string templateParam, CancellationToken cancellationToken) { if (options.Value.SMS?.Enable != true) { return; } if (options.Value.SMS != null && options.Value.SMS.Version.IsNotNull() && options.Value.SMS.RegionId.IsNotNull() && options.Value.SMS.SignName.IsNotNull() && options.Value.SMS.AccessKeyId.IsNotNull() && options.Value.SMS.AccessSecret.IsNotNull()) { var _templateCode = options.Value.SMS.TemplateCodes[templateCode.ToString()]; var _params = new Dictionary { {"Action", "SendSms"}, {"Version", options.Value.SMS.Version}, {"RegionId", options.Value.SMS.RegionId}, {"PhoneNumbers", phoneNumber}, {"SignName", options.Value.SMS.SignName}, {"TemplateCode", _templateCode} }; if (!string.IsNullOrWhiteSpace(templateParam)) { _params.Add("TemplateParam", templateParam); } var timestamp = DateTime.Now.ToUniversalTime() .ToString("yyyy-MM-dd'T'HH:mm:ss'Z'", CultureInfo.CreateSpecificCulture("en-US")); _params.Add("AccessKeyId", options.Value.SMS.AccessKeyId); _params.Add("Timestamp", timestamp); _params.Add("Format", "JSON"); _params.Add("SignatureMethod", "HMAC-SHA1"); _params.Add("SignatureVersion", "1.0"); _params.Add("SignatureNonce", Guid.NewGuid().ToString()); //排序 var sortDic = new SortedDictionary(_params, StringComparer.Ordinal); //生成Url参数 var urlParams = ""; foreach (var dic in sortDic) { urlParams += $"{PercentEncode(dic.Key)}={PercentEncode(dic.Value)}&"; } urlParams = urlParams.TrimEnd('&'); //签名 var stringToSign = $"GET&{PercentEncode("/")}&{PercentEncode(urlParams)}"; string signature = PercentEncode(ToHmacsha1(stringToSign, options.Value.SMS.AccessSecret + "&")); var res = await httpRemoteService.GetAsStringAsync($"http://dysmsapi.aliyuncs.com/?Signature={signature}&{urlParams}"); var callback = res.JsonTo(new { Code = "", Message = "", RequestId = "", BizId = "" }); if (callback == null || callback.Code != "OK") { throw Oops.Oh(EnumErrorCodeType.s510, $"发送短信失败:{callback?.Message},请联系管理员"); } } } /// /// 排除敏感字符串 /// /// 文本 /// 排除后文本 private string PercentEncode(string value) { var stringBuilder = new StringBuilder(); var text = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~"; var bytes = Encoding.GetEncoding("UTF-8").GetBytes(value); foreach (var b in bytes) { var c = (char)b; if (text.IndexOf(c) >= 0) stringBuilder.Append(c); else stringBuilder.Append("%").Append( string.Format(CultureInfo.InvariantCulture, "{0:X2}", (int)c)); } return stringBuilder.ToString(); } /// /// HMAC-SHA1加密 /// /// /// /// private static string ToHmacsha1(string content, string key) { var hmacsha1 = new HMACSHA1(Encoding.UTF8.GetBytes(key)); var bytes = hmacsha1.ComputeHash(Encoding.UTF8.GetBytes(content)); return Convert.ToBase64String(bytes); } } }