Aps.Net Core3.1 WebApi發送阿里雲短信驗證碼


1、前言

轉眼又要過了一年了 好久沒寫博客了,人不學就要落后,今天有時間把以前弄的發送阿里雲短信驗證碼登錄記錄一下。

2、准備條件

1)去阿里雲官網注冊一個賬號。有賬號直接登錄就行,以前新人好像有免費的短信可以學習 ,現在我們只能購買了 先開通短信服務然后去購買 購買鏈接 

2)跳轉到控制台的短信服務點擊國內消息簽名模板 右邊點添加簽名。后面在模板管理哪里添加模板  。

模板這個簽名的意思就是 發送短信驗證碼的頭部,類似於發票的抬頭文字  一般個人只能申請一個驗證碼簽名。企業的不知道(沒試過。。。)。模板就是具體的內容,里面可以使登錄的驗證碼  注冊的驗證碼 找回密碼的驗證碼   加上簽名就是 【xxx時光管】 您好,你登錄的驗證碼是520886。【】不用寫在簽名里面 發短信的時候會自動加上。個人的簽名不能包含一些企業的文字  到時候他會告訴你簽名、模板注意事項。

 

 3)查看個人的accessKeyId跟accessSecret 右上角點個人頭像點擊AccessKey管理。沒有的就創建一個,有的話就看列表點擊查看Secret  然后驗證一下就可以了,記住這兩個key

 

3、Core 代碼

1)寫一個發送驗證碼的輔助類SMS

public class SMS
    {
        /// <summary>
        /// 發送短信
        /// </summary>
        /// <param name="phone">手機號碼</param>
        /// <param name="code">驗證碼</param>
        /// <param name="templateName">驗證碼模板</param>
        /// <param name="accessKeyId">accessKeyId= LTAIA8bLXzkMsKR4JB8</param>
        /// <param name="secret">secret= SamAiMzbdC0yS1lo6u9O9r</param>
        /// <param name="signName">簽名名稱</param>
        /// <returns></returns>
        public static SendMessageDto SendSMS(string phone, string code, string templateName, string accessKeyId, string secret, string signName)
        {
            var ret = new SendMessageDto();
            IClientProfile profile = DefaultProfile.GetProfile("cn-hangzhou", accessKeyId, secret);
            DefaultAcsClient client = new DefaultAcsClient(profile);
            CommonRequest request = new CommonRequest
            {
                Method = MethodType.POST,
                Domain = "dysmsapi.aliyuncs.com",
                Version = "2017-05-25",
                Action = "SendSms"
            };
            request.AddQueryParameters("PhoneNumbers", phone);
            request.AddQueryParameters("SignName", signName);
            request.AddQueryParameters("TemplateCode", templateName);
            request.AddQueryParameters("TemplateParam", $"{{'code':'{code}'}}");
            // request.Protocol = ProtocolType.HTTP;

            try
            {
                CommonResponse response = client.GetCommonResponse(request);
                var result = Encoding.Default.GetString(response.HttpResponse.Content);
                var resultJson = JsonConvert.DeserializeObject<SendMessageDto>(result);
                ret = resultJson;
            }
            catch (ServerException e)
            {
                Console.WriteLine(e);
            }
            catch (ClientException e)
            {
                Console.WriteLine(e);
            }
            return ret;
        }
    }
    public class SendMessageDto
    {
        /// <summary>
        /// 狀態碼的描述。
        /// </summary>
        public string Message { get; set; }
        /// <summary>
        ///    請求ID。
        /// </summary>
        public string RequestId { get; set; }
        /// <summary>
        ///  發送回執ID,可根據該ID在接口QuerySendDetails中查詢具體的發送狀態。
        /// </summary>
        public string BizId { get; set; }
        /// <summary>
        /// 請求狀態碼。 返回OK代表請求成功。
        /// </summary>
        public string Code { get; set; }
    }

 

 2)驗證碼配置類SMSConfig

 /// <summary>
    /// 驗證碼配置類
    /// </summary>
    public class SMSConfig
    {
        public string AccessKeyId { get; set; }
        public string AccessSecret { get; set; }
        public string SignName { get; set; }
        public Login Login { get; set; }
        public Regist Regist { get; set; }
        public Reset Reset { get; set; }
    }
    /// <summary>
    /// 驗證碼模板
    /// </summary>
    public class Login
    {
        /// <summary>
        /// 類型 1登錄 2注冊 3找回、重置密碼
        /// </summary>
        //public int Type { get; set; }
        /// <summary>
        /// 模板名稱
        /// </summary>
        public string TemplateName { get; set; }
    }
    public class Regist
    {

        /// <summary>
        /// 模板名稱
        /// </summary>
        public string TemplateName { get; set; }
    }
    public class Reset
    {

        /// <summary>
        /// 模板名稱
        /// </summary>
        public string TemplateName { get; set; }
    }

 

3)數據庫建立一張表存儲驗證碼

  public partial class SysSendSms
    {
        /// <summary>
        /// Id
        /// </summary>
        [Key]
        [Column("Id", Order = 0)]
        [Required()]
        [Display(Name = "Id")]
        public int Id { get; set; }
        /// <summary>
        /// 驗證碼
        /// </summary>
        [Column("Code")]
        [StringLength(20, ErrorMessage = "{0}長度不能超過20個字符")]
        [Display(Name = "驗證碼")]
        public string Code { get; set; }
        /// <summary>
        /// 手機號
        /// </summary>
        [Column("Phone")]
        [StringLength(20, ErrorMessage = "{0}長度不能超過20個字符")]
        [Display(Name = "手機號")]
        public string Phone { get; set; }
        /// <summary>
        /// 發送時間
        /// </summary>
        [Column("SendTime")]
        [Display(Name = "發送時間")]
        [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:yyyy-MM-dd HH:mm:ss:ffff}")]
        public DateTime? SendTime { get; set; } = DateTime.Now;
        /// <summary>
        /// gid
        /// </summary>
        [Column("Guid")]
        [StringLength(36, ErrorMessage = "{0}長度不能超過36個字符")]
        [Display(Name = "Guid")]
        public string Guid { get; set; }
        /// <summary>
        /// 次數
        /// </summary>
        [Column("Count")]
        [Display(Name = "次數")]
        public int? Count { get; set; }
        /// <summary>
        /// 是否用了
        /// </summary>
        [Column("IsUsed")]
        [Display(Name = "領取數量")]
        public bool? IsUsed { get; set; }
        /// <summary>
        /// 類型1登陸 2注冊
        /// </summary>
        [Column("Type")]
        [Display(Name = "類型1登陸 2注冊")]
        public int? Type { get; set; }
    }

 

這個type可以自己定義,我這邊演示一個登陸的發送驗證碼

4)發送短信方法

  public async Task<Result> SendSMSAsync(SendSmsDto sendSmsDto)
        {
            var code = new Random().Next(1000, 9999).ToString();
            var templateName = sendSmsDto.Type switch
            {
                1 => _smsConfig.Value.Login.TemplateName,
                2 => _smsConfig.Value.Regist.TemplateName,
                _ => _smsConfig.Value.Login.TemplateName
            };
            return await Send(sendSmsDto.Phone, sendSmsDto.Type, code, templateName);            
        }
 /// <summary>
        /// 發送短信驗證碼
        /// </summary>
        /// <param name="phone">手機</param>
        /// <param name="type">類型(后面數據庫好區分,防止亂發)</param>
        /// <param name="code">驗證碼</param>
        /// <param name="templateName">模板申請下來對應的Code</param>
        /// <returns></returns>
        private async Task<Result> Send(string phone, int type, string code, string templateName)
        {
            //手機號相同 密碼相同 沒有用過的
            var model = _db.SysSendSms.OrderByDescending(x=>x.SendTime).FirstOrDefault(x => x.Phone == phone && x.Type == type && x.IsUsed == false);
            var guid = Guid.NewGuid().ToString("N");
            var dt = DateTime.Now;//時間
            if (type == 1)//登錄
            {
                var ckuser = _db.Customer.Any(x => x.Phone == phone);
                if(!ckuser)
                {
                    var cus = new Customer
                    {
                        Phone = phone,
                        LoginID = phone,
                        Gid = Guid.NewGuid().ToString("N"),
                        Pwd = SecurityHelper.MD5(phone)
                    };
                    _db.Customer.Add(cus);
                    _db.SaveChanges();
                    return await CkSend(phone, type, code, templateName, model, guid, dt);
                }
                else
                    return await CkSend(phone, type, code, templateName, model, guid, dt);
            }
            else
                return Result.ToFail("請求參數錯誤,type");
        }

 

檢查發送驗證碼的方法 為了防止惡意亂發

 /// <summary>
        /// 看看以前發過沒有
        /// </summary>
        /// <param name="phone"></param>
        /// <param name="type"></param>
        /// <param name="code"></param>
        /// <param name="templateName"></param>
        /// <param name="model"></param>
        /// <param name="guid"></param>
        /// <param name="dt"></param>
        /// <returns></returns>
        private async Task<Result> CkSend(string phone, int type, string code, string templateName, SysSendSms model, string guid, DateTime dt)
        {
            if (model == null)//沒有發送過
            {
                var sendMess = SMS.SendSMS(phone, code, templateName, _smsConfig.Value.AccessKeyId, _smsConfig.Value.AccessSecret, _smsConfig.Value.SignName);
                if (sendMess.Code == "OK")
                {
                    await SaveData(phone, type, code, guid, dt);
                    return Result.ToSuccess(new { code });
                }
                else
                    return Result.ToFail("短信驗證碼發送失敗" + sendMess.Code + sendMess.Message);
            }
            else
            {
                var co = model.Count;//發送次數
                if (co >= 5 && dt.AddHours(-5) < model.SendTime)//大於3次並且時間在3個小時內
                    return Result.ToFail("短信驗證碼發送頻繁,請3個小時再試");
                else /*(co >= 8 && dt.AddDays(-1) < model.SendTime)//大於8次並且時間在24個小時內*/
                {
                    var sendMess = SMS.SendSMS(phone, code, templateName, _smsConfig.Value.AccessKeyId, _smsConfig.Value.AccessSecret, _smsConfig.Value.SignName);
                    if (sendMess.Code == "OK")
                    {
                        model.Count++;
                        model.SendTime = DateTime.Now;
                        model.Code = code;
                        _db.SaveChanges();
                        return Result.ToSuccess(new { code });
                    }
                    else
                        return Result.ToFail("短信驗證碼發送失敗"+ sendMess.Code+sendMess.Message);
                }
            }
        }

        /// <summary>
        /// 保存發送信息到數據庫
        /// </summary>
        /// <param name="phone"></param>
        /// <param name="type"></param>
        /// <param name="code"></param>
        /// <param name="guid"></param>
        /// <param name="dt"></param>
        /// <returns></returns>

        private async Task SaveData(string phone, int type, string code, string guid, DateTime dt)
        {
            var modelSMS = new SysSendSms
            {
                Code = code,
                Phone = phone,
                SendTime = dt,
                Guid = guid,
                Count = 1,
                IsUsed = false,
                Type = type
            };
            _db.SysSendSms.Add(modelSMS);
            await _db.SaveChangesAsync();
        }

 

我這邊的邏輯其實是寫死的  type只能是1  就是這個用戶不在數據庫里面先創建用戶 然后發送驗證碼。在里面就直接發送驗證碼。下面是登錄方法

  public async Task<Result> LoginAsync(LoginDto loginDto)
        {
            try
            {
                var model = _db.Customer.FirstOrDefault(x => x.LoginID == loginDto.Phone);
                if (model != null)
                {
                    var send = _db.SysSendSms.OrderByDescending(x => x.SendTime).FirstOrDefault(x => x.Code == loginDto.Code);
                    if (send == null)
                       return Result.ToFail("手機驗證碼錯誤");
                    else
                    {
                        Dictionary<string, string> keyValues = new Dictionary<string, string>()
                            {
                                {"Id",model.LoginID }
                            };
                        var token = _tokenHelper.CreateToken(keyValues);
                        send.IsUsed = true;
                        model.Token = token.TokenStr;
                        await _db.SaveChangesAsync();
                        return Result.ToSuccess(token);
                    }
                }
                else
                   return Result.ToFail("用戶不存在");
            }
            catch (Exception ex)
            {
               return Result.ToError(ex);
            }
        }
 public class LoginDto
    {
        /// <summary>
        /// 手機號碼
        /// </summary>
        public string Phone { get; set; }
        /// <summary>
        /// 驗證碼
        /// </summary>
        public string Code { get; set; }
    }

 

如果驗證成功了就要把數據庫發送短信驗證碼的字段IsUse改成true

我的模板 簽名都寫在配置文件里面

 //短信驗證碼配置  所有配置來自阿里雲官方
  "SMSConfig": {
    "AccessKeyId": "LTAI4GB1mA8bLXzkMsKR4JB8",
    "AccessSecret": "SamAiMzbZ4ufoNRndC0yS1lo6u9O9r",
    "SignName": "真猴吃", //簽名名稱
    "Login": {
      "TemplateName": "SMS_200700770" //登錄模板代碼
    },
    "Regist": {
      "TemplateName": "SMS_180347559" //注冊模板代碼
    },
    "Reset": {
      "TemplateName": "SMS_192835767" //找回密碼模板代碼
    }
  },

 

這與上面的接收類一樣的格式  當然在Core里面別忘了配置連接字符串  再調用的類或者接口在startup里面注冊

  services.Configure<SMSConfig>(Configuration.GetSection("SMSConfig"));

4、參考、在線調試

 網址:https://api.aliyun.com/new#/?product=Dysmsapi&api=SendSms&params=%7B%22RegionId%22%3A%22cn-hangzhou%22%2C%22PhoneNumbers%22%3A%22%22%2C%22SignName%22%3A%22%22%2C%22TemplateCode%22%3A%22%22%7D&tab=DEMO&lang=JAVA

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM