ABP中有短信发送接口ISmsSender
public interface ISmsSender
{
Task<string> SendAsync(string number, string message);
}
使用阿里云短信服务实现这个接口,使得能够通过阿里云短信服务发送通知短信
ISmsSender一般是在Core项目中,所以实现类也放一起
首先是实现一个ISmsSender
1 using Abp.Dependency; 2 using Castle.Core.Logging; 3 using System; 4 using System.Threading.Tasks; 5 using System.Collections.Generic; 6 using System.Text; 7 using System.Security.Cryptography; 8 using System.Globalization; 9 using System.Net.Http; 10 using Newtonsoft.Json; 11 using Microsoft.Extensions.Configuration; 12 using MyCompany.MyProject.Configuration; 13 using Microsoft.AspNetCore.Hosting; 14 15 namespace MyCompany.MyProject.Identity 16 { 17 public class AliyunSmsSender : ISmsSender, ITransientDependency 18 { 19 public ILogger Logger { get; set; } 20 string appkey ; 21 string secret; 22 string serverUrl = "dysmsapi.aliyuncs.com"; 23 string signName; 24 string TemplateCode; 25 private Dictionary<string, string> smsDict = new Dictionary<string, string> 26 { 27 { "RegionId", "cn-hangzhou" }, 28 { "Action", "SendSms" }, 29 { "Version", "2017-05-25" }, 30 }; 31 private readonly IConfigurationRoot configuration; 32 33 public AliyunSmsSender(ILogger Logger, IHostingEnvironment env) 34 { 35 configuration = env.GetAppConfiguration(); 36 appkey = configuration["SmsConfiguration:AliSms:appkey"]; 37 secret = configuration["SmsConfiguration:AliSms:secret"]; 38 signName = configuration["SmsConfiguration:AliSms:SignName"]; 39 TemplateCode = configuration["SmsConfiguration:AliSms:TemplateCode"]; 40 41 smsDict.Add("SignName", signName);//签名 42 smsDict.Add("TemplateCode", TemplateCode);//模板 43 smsDict.Add("TemplateParam", "");//参数内容 44 smsDict.Add("PhoneNumbers", "");//发送到的手机号 45 46 } 47 public async Task<string> SendAsync(string number, string message) 48 { 49 try 50 { 51 smsDict["PhoneNumbers"] = number; 52 smsDict["TemplateParam"] = JsonConvert.SerializeObject(new { code = message }); 53 var signatiure = new SignatureHelper(); 54 string res = await signatiure.Request(appkey, secret, serverUrl, smsDict, logger: Logger); 55 Logger.Info("验证短信发送返回:" + res); 56 return res; 57 } 58 catch (Exception e) 59 { 60 Logger.Error(e.Message); 61 throw; 62 } 63 } 64 65 /// <summary> 66 /// 签名助手 67 /// https://help.aliyun.com/document_detail/30079.html?spm=5176.7739992.2.3.HM7WTG 68 /// </summary> 69 public class SignatureHelper 70 { 71 private const string ISO8601_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'"; 72 private const string ENCODING_UTF8 = "UTF-8"; 73 public static string PercentEncode(String value) 74 { 75 StringBuilder stringBuilder = new StringBuilder(); 76 string text = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~"; 77 byte[] bytes = Encoding.GetEncoding(ENCODING_UTF8).GetBytes(value); 78 foreach (char c in bytes) 79 { 80 if (text.IndexOf(c) >= 0) 81 { 82 stringBuilder.Append(c); 83 } 84 else 85 { 86 stringBuilder.Append("%").Append( 87 string.Format(CultureInfo.InvariantCulture, "{0:X2}", (int)c)); 88 } 89 } 90 return stringBuilder.ToString(); 91 } 92 public static string FormatIso8601Date(DateTime date) 93 { 94 return date.ToUniversalTime().ToString(ISO8601_DATE_FORMAT, CultureInfo.CreateSpecificCulture("en-US")); 95 } 96 97 private static IDictionary<string, string> SortDictionary(Dictionary<string, string> dic) 98 { 99 IDictionary<string, string> sortedDictionary = new SortedDictionary<string, string>(dic, StringComparer.Ordinal); 100 return sortedDictionary; 101 } 102 103 public static string SignString(string source, string accessSecret) 104 { 105 using (var algorithm = new HMACSHA1()) 106 { 107 algorithm.Key = Encoding.UTF8.GetBytes(accessSecret.ToCharArray()); 108 return Convert.ToBase64String(algorithm.ComputeHash(Encoding.UTF8.GetBytes(source.ToCharArray()))); 109 } 110 } 111 112 113 public async Task<string> HttpGetAsync(string url, ILogger logger) 114 { 115 string responseBody = string.Empty; 116 using (var http = new HttpClient()) 117 { 118 try 119 { 120 http.DefaultRequestHeaders.Add("x-sdk-client", "Net/2.0.0"); 121 var response = await http.GetAsync(url); 122 response.EnsureSuccessStatusCode(); 123 responseBody = await response.Content.ReadAsStringAsync(); 124 } 125 catch (HttpRequestException e) 126 { 127 Console.WriteLine("\nException !"); 128 Console.WriteLine("Message :{0} ", e.Message); 129 throw; 130 } 131 } 132 return responseBody; 133 134 } 135 136 public async Task<string> Request(string accessKeyId, string accessKeySecret, string domain, Dictionary<string, string> paramsDict, ILogger logger, bool security = false) 137 { 138 var apiParams = new Dictionary<string, string> 139 { 140 { "SignatureMethod", "HMAC-SHA1" }, 141 { "SignatureNonce", Guid.NewGuid().ToString() }, 142 { "SignatureVersion", "1.0" }, 143 { "AccessKeyId", accessKeyId }, 144 { "Timestamp", FormatIso8601Date(DateTime.Now) }, 145 { "Format", "JSON" }, 146 }; 147 148 foreach (var param in paramsDict) 149 { 150 if (!apiParams.ContainsKey(param.Key)) 151 { 152 apiParams.Add(param.Key, param.Value); 153 } 154 } 155 var sortedDictionary = SortDictionary(apiParams); 156 string sortedQueryStringTmp = ""; 157 foreach (var param in sortedDictionary) 158 { 159 sortedQueryStringTmp += "&" + PercentEncode(param.Key) + "=" + PercentEncode(param.Value); 160 } 161 162 string stringToSign = "GET&%2F&" + PercentEncode(sortedQueryStringTmp.Substring(1)); 163 string sign = SignString(stringToSign, accessKeySecret + "&"); 164 string signature = PercentEncode(sign); 165 string url = (security ? "https" : "http") + $"://{domain}/?Signature={signature}{sortedQueryStringTmp}"; 166 string result; 167 try 168 { 169 result = await HttpGetAsync(url, logger); 170 } 171 catch (Exception) 172 { 173 174 throw; 175 } 176 return result; 177 178 } 179 } 180 } 181 }
然后在core项目的Mudule中配置注入关系
最后在配置文件中加上配置节点即可
"SmsConfiguration": { "AliSms": { "appkey": "", "secret": "", "SignName": "", "TemplateCode": "" } }
对应上appkey secret,签名,模板code
最后在需要调用的地方注入ISmsSender, 调用其SendAsync方法,传入手机号和内容即可。