|
|
|
|
using Newtonsoft.Json;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Globalization;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Net;
|
|
|
|
|
using System.Net.Http;
|
|
|
|
|
using System.Net.Security;
|
|
|
|
|
using System.Security.Cryptography;
|
|
|
|
|
using System.Security.Cryptography.X509Certificates;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using System.Web;
|
|
|
|
|
using Common;
|
|
|
|
|
namespace Common.Tools.Message
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 阿里云短信发送
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class aliyunSmsservice : ISmsService
|
|
|
|
|
{
|
|
|
|
|
private string RegionId = "cn-hangzhou";
|
|
|
|
|
private string Version = "2017-05-25";
|
|
|
|
|
private string Action = "SendSms";
|
|
|
|
|
private string Format = "JSON";
|
|
|
|
|
private string Domain = "dysmsapi.aliyuncs.com";
|
|
|
|
|
|
|
|
|
|
private int MaxRetryNumber = 3;
|
|
|
|
|
private bool AutoRetry = true;
|
|
|
|
|
private const string SEPARATOR = "&";
|
|
|
|
|
private int TimeoutInMilliSeconds = 100000;
|
|
|
|
|
|
|
|
|
|
private string AccessKeyId= "LTAIvkPRG12jmvyP";
|
|
|
|
|
private string AccessKeySecret= "j2bkq5jDbfj05r6RpcXtLx5BiLPZzo";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 短信发送 阿里云
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="MobileList">接收手机号列表多个用,间隔最多不能超过1000个</param>
|
|
|
|
|
|
|
|
|
|
/// <param name="TemplateCode">模版ID</param>
|
|
|
|
|
/// <param name="TemplateParam">模版变量</param>
|
|
|
|
|
/// <param name="SignName">短信签名</param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public async Task<ReturnResult<object>> SendSmsAsync(List<string> MobileList,string SmsValue=null, string TemplateCode=null, Dictionary<string, string> TemplateParam = null, string SignName = null)
|
|
|
|
|
{
|
|
|
|
|
var r = new ReturnResult<object>();
|
|
|
|
|
if ( TemplateCode == null)
|
|
|
|
|
{
|
|
|
|
|
r.Not("模版Id不能为空");
|
|
|
|
|
}
|
|
|
|
|
StringBuilder jsonstr = new StringBuilder();
|
|
|
|
|
|
|
|
|
|
#region 阿里云短信发送实现
|
|
|
|
|
foreach (var item in MobileList)
|
|
|
|
|
{
|
|
|
|
|
var sms = new SmsObject
|
|
|
|
|
{
|
|
|
|
|
Mobile = item,
|
|
|
|
|
Signature = SignName,
|
|
|
|
|
TempletKey = TemplateCode,
|
|
|
|
|
Data = TemplateParam,
|
|
|
|
|
OutId = "OutId"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var res = await _SendSms(sms);
|
|
|
|
|
|
|
|
|
|
System.Threading.Thread.Sleep(10);
|
|
|
|
|
}
|
|
|
|
|
r.OK();
|
|
|
|
|
#endregion
|
|
|
|
|
return await Task.Run(() => r);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task<(bool success, string response)> _SendSms(SmsObject sms)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var paramers = new Dictionary<string, string>();
|
|
|
|
|
paramers.Add("PhoneNumbers", sms.Mobile);
|
|
|
|
|
paramers.Add("SignName", sms.Signature);
|
|
|
|
|
paramers.Add("TemplateCode", sms.TempletKey);
|
|
|
|
|
paramers.Add("TemplateParam", JsonConvert.SerializeObject(sms.Data));
|
|
|
|
|
paramers.Add("OutId", sms.OutId);
|
|
|
|
|
paramers.Add("AccessKeyId", AccessKeyId);
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
string url = GetSignUrl(paramers, AccessKeySecret);
|
|
|
|
|
|
|
|
|
|
int retryTimes = 1;
|
|
|
|
|
var reply = await HttpGetAsync(url);
|
|
|
|
|
while (500 <= reply.StatusCode && AutoRetry && retryTimes < MaxRetryNumber)
|
|
|
|
|
{
|
|
|
|
|
url = GetSignUrl(paramers, AccessKeySecret);
|
|
|
|
|
reply = await HttpGetAsync(url);
|
|
|
|
|
retryTimes++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(reply.response))
|
|
|
|
|
{
|
|
|
|
|
var res = JsonConvert.DeserializeObject<Dictionary<string, string>>(reply.response);
|
|
|
|
|
if (res != null && res.ContainsKey("Code") && "OK".Equals(res["Code"]))
|
|
|
|
|
{
|
|
|
|
|
return (true, response: reply.response);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (false, response: reply.response);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
return (false, response: ex.Message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private string GetSignUrl(Dictionary<string, string> parameters, string accessSecret)
|
|
|
|
|
{
|
|
|
|
|
var imutableMap = new Dictionary<string, string>(parameters);
|
|
|
|
|
imutableMap.Add("Timestamp", FormatIso8601Date(DateTime.Now));
|
|
|
|
|
imutableMap.Add("SignatureMethod", "HMAC-SHA1");
|
|
|
|
|
imutableMap.Add("SignatureVersion", "1.0");
|
|
|
|
|
imutableMap.Add("SignatureNonce", Guid.NewGuid().ToString());
|
|
|
|
|
imutableMap.Add("Action", Action);
|
|
|
|
|
imutableMap.Add("Version", Version);
|
|
|
|
|
imutableMap.Add("Format", Format);
|
|
|
|
|
imutableMap.Add("RegionId", RegionId);
|
|
|
|
|
|
|
|
|
|
IDictionary<string, string> sortedDictionary = new SortedDictionary<string, string>(imutableMap, StringComparer.Ordinal);
|
|
|
|
|
StringBuilder canonicalizedQueryString = new StringBuilder();
|
|
|
|
|
foreach (var p in sortedDictionary)
|
|
|
|
|
{
|
|
|
|
|
canonicalizedQueryString.Append("&")
|
|
|
|
|
.Append(PercentEncode(p.Key)).Append("=")
|
|
|
|
|
.Append(PercentEncode(p.Value));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
StringBuilder stringToSign = new StringBuilder();
|
|
|
|
|
stringToSign.Append("GET");
|
|
|
|
|
stringToSign.Append(SEPARATOR);
|
|
|
|
|
stringToSign.Append(PercentEncode("/"));
|
|
|
|
|
stringToSign.Append(SEPARATOR);
|
|
|
|
|
stringToSign.Append(PercentEncode(canonicalizedQueryString.ToString().Substring(1)));
|
|
|
|
|
|
|
|
|
|
string signature = SignString(stringToSign.ToString(), accessSecret + "&");
|
|
|
|
|
|
|
|
|
|
imutableMap.Add("Signature", signature);
|
|
|
|
|
|
|
|
|
|
return ComposeUrl(Domain, imutableMap);
|
|
|
|
|
}
|
|
|
|
|
private static string FormatIso8601Date(DateTime date)
|
|
|
|
|
{
|
|
|
|
|
return date.ToUniversalTime().ToString("yyyy-MM-dd'T'HH:mm:ss'Z'", CultureInfo.CreateSpecificCulture("en-US"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 签名
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static string SignString(string source, string accessSecret)
|
|
|
|
|
{
|
|
|
|
|
using (var algorithm = new HMACSHA1(Encoding.UTF8.GetBytes(accessSecret.ToCharArray())))
|
|
|
|
|
{
|
|
|
|
|
return Convert.ToBase64String(algorithm.ComputeHash(Encoding.UTF8.GetBytes(source.ToCharArray())));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static string ComposeUrl(string endpoint, Dictionary<String, String> parameters)
|
|
|
|
|
{
|
|
|
|
|
StringBuilder urlBuilder = new StringBuilder("");
|
|
|
|
|
urlBuilder.Append("http://").Append(endpoint);
|
|
|
|
|
if (-1 == urlBuilder.ToString().IndexOf("?"))
|
|
|
|
|
{
|
|
|
|
|
urlBuilder.Append("/?");
|
|
|
|
|
}
|
|
|
|
|
string query = ConcatQueryString(parameters);
|
|
|
|
|
return urlBuilder.Append(query).ToString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static string ConcatQueryString(Dictionary<string, string> parameters)
|
|
|
|
|
{
|
|
|
|
|
if (null == parameters)
|
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
|
|
|
|
|
|
foreach (var entry in parameters)
|
|
|
|
|
{
|
|
|
|
|
String key = entry.Key;
|
|
|
|
|
String val = entry.Value;
|
|
|
|
|
|
|
|
|
|
sb.Append(HttpUtility.UrlEncode(key, Encoding.UTF8));
|
|
|
|
|
if (val != null)
|
|
|
|
|
{
|
|
|
|
|
sb.Append("=").Append(HttpUtility.UrlEncode(val, Encoding.UTF8));
|
|
|
|
|
}
|
|
|
|
|
sb.Append("&");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int strIndex = sb.Length;
|
|
|
|
|
if (parameters.Count > 0)
|
|
|
|
|
sb.Remove(strIndex - 1, 1);
|
|
|
|
|
|
|
|
|
|
return sb.ToString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string PercentEncode(string value)
|
|
|
|
|
{
|
|
|
|
|
StringBuilder stringBuilder = new StringBuilder();
|
|
|
|
|
string text = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";
|
|
|
|
|
byte[] bytes = Encoding.GetEncoding("UTF-8").GetBytes(value);
|
|
|
|
|
foreach (char c in bytes)
|
|
|
|
|
{
|
|
|
|
|
if (text.IndexOf(c) >= 0)
|
|
|
|
|
{
|
|
|
|
|
stringBuilder.Append(c);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
stringBuilder.Append("%").Append(
|
|
|
|
|
string.Format(CultureInfo.InvariantCulture, "{0:X2}", (int)c));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return stringBuilder.ToString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task<(int StatusCode, string response)> HttpGetAsync(string url)
|
|
|
|
|
{
|
|
|
|
|
HttpClientHandler handler = new HttpClientHandler();
|
|
|
|
|
handler.Proxy = null;
|
|
|
|
|
handler.AutomaticDecompression = DecompressionMethods.GZip;
|
|
|
|
|
|
|
|
|
|
using (var http = new HttpClient(handler))
|
|
|
|
|
{
|
|
|
|
|
http.Timeout = new TimeSpan(TimeSpan.TicksPerMillisecond * TimeoutInMilliSeconds);
|
|
|
|
|
HttpResponseMessage response = await http.GetAsync(url);
|
|
|
|
|
return ((int)response.StatusCode, await response.Content.ReadAsStringAsync());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|