zhaolei
2020-11-20 921de2254ff5712a44ed8575ee8efe34252f6603
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
#if !NETSTANDARD2_0
using System.Text.Json;
#else 
using Newtonsoft.Json;
#endif
using System.Threading.Tasks;
 
namespace Prow.Web.SMS
{
    /// <summary>
    /// 手机号登陆帮助类
    /// </summary>
    public class DefaultSMSProvider : ISMSProvider
    {
        private static readonly ConcurrentDictionary<string, AutoExpireValidateCode> _pool = new ConcurrentDictionary<string, AutoExpireValidateCode>();
 
        /// <summary>
        /// 获得 短信配置信息
        /// </summary>
        public SMSOptions Options { get { return _options; } }
 
        private readonly DefaultSMSOptions _options;
        private readonly HttpClient _client;
 
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="configuration"></param>
        /// <param name="factory"></param>
        public DefaultSMSProvider(IConfiguration configuration, IHttpClientFactory factory)
        {
            _options = configuration.GetSection(nameof(SMSOptions)).Get<DefaultSMSOptions>();
            _client = factory.CreateClient();
        }
 
        /// <summary>
        /// 下发验证码方法
        /// </summary>
        /// <param name="phoneNumber"></param>
        /// <returns></returns>
        public async Task<SMSResult> SendCodeAsync(string phoneNumber)
        {
            Options.Timestamp = (DateTimeOffset.UtcNow.Ticks - 621355968000000000) / 10000000;
            Options.Phone = phoneNumber;
            var requestParameters = new Dictionary<string, string>()
            {
                { "CompanyCode", _options.CompanyCode },
                { "Phone", Options.Phone },
                { "TimeStamp", Options.Timestamp.ToString() },
                { "Sign", Sign() }
            };
 
            var url = QueryHelpers.AddQueryString(Options.RequestUrl, requestParameters);
            var req = await _client.GetAsync(url);
            var content = await req.Content.ReadAsStringAsync();
#if !NETSTANDARD2_0
            var result = JsonSerializer.Deserialize<DefaultSMSResult>(content, new JsonSerializerOptions() { PropertyNameCaseInsensitive = true });
#else
            var result = JsonConvert.DeserializeObject<DefaultSMSResult>(content);
#endif
            var ret = new SMSResult() { Result = result.Code == 1, Msg = result.Msg };
            if (ret.Result)
            {
                _pool.AddOrUpdate(Options.Phone, key => new AutoExpireValidateCode(Options.Phone, result.Data, Options.Expires, phone => _pool.TryRemove(phone, out var _)), (key, v) => v.Reset(result.Data));
            }
            else
            {
                new Exception(result.Msg).Log(new NameValueCollection()
                {
                    ["UserId"] = Options.Phone,
                    ["url"] = url,
                    ["content"] = content
                });
            }
            return ret;
        }
 
        /// <summary>
        /// 验证验证码方法
        /// </summary>
        /// <param name="phone">手机号</param>
        /// <param name="code">验证码</param>
        /// <returns></returns>
        public bool Validate(string phone, string code) => _pool.TryGetValue(phone, out var signKey) && Hash($"{code}{_options.MD5Key}") == signKey.Code;
 
        private string Sign()
        {
            return Hash($"{_options.CompanyCode}{Options.Phone}{Options.Timestamp}{_options.MD5Key}");
        }
 
        private static string Hash(string data)
        {
            using var md5 = MD5.Create();
            var sign = BitConverter.ToString(md5.ComputeHash(Encoding.UTF8.GetBytes(data)));
            sign = sign.Replace("-", "").ToLowerInvariant();
            return sign;
        }
 
        private class DefaultSMSResult
        {
            public int Code { get; set; }
 
            public string Data { get; set; } = "";
 
            public string Msg { get; set; } = "";
        }
    }
 
    /// <summary>
    /// 
    /// </summary>
    public class DefaultSMSOptions : SMSOptions
    {
        /// <summary>
        /// 获得/设置 公司编码
        /// </summary>
        public string CompanyCode { get; set; } = "";
 
        /// <summary>
        /// 获得/设置 签名密钥
        /// </summary>
        public string MD5Key { get; set; } = "";
    }
}