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、參考、在線調試