一 Abp.Zero 手机号免密登录验证与号码绑定功能的实现:验证码模块( 二 )

public const string LOGIN = "LOGIN";public const string IDENTITY_VERIFICATION = "IDENTITY_VERIFICATION";public const string BIND_PHONENUMBER = "BIND_PHONENUMBER";public const string UNBIND_PHONENUMBER = "UNBIND_PHONENUMBER";定义一个验证码Token缓存管理类 , 以及对应的缓存条目类 , 用于承载验证码的校验内容
【一 Abp.Zero 手机号免密登录验证与号码绑定功能的实现:验证码模块】public class SmsCaptchaTokenCache : MemoryCacheBase<SmsCaptchaTokenCacheItem>, ISingletonDependency{public SmsCaptchaTokenCache() : base(nameof(SmsCaptchaTokenCache)){}}缓存条目将存储电话号码 , 用户Id(非登录用途)以及用途
public class SmsCaptchaTokenCacheItem{public string PhoneNumber { get; set; }public long UserId { get; set; }public string Purpose { get; set; }}阿里云和腾讯云提供了短信服务Sms , 是国内比较常见的短信服务提供商 , 不需要自己写了 , 网上有大把的封装好的库 , 这里使用AbpBoilerplate.Sms作为短信服务库 。
创建短信验证码的领域服务类SmsCaptchaManager并实现ICaptchaManager接口 , 同时注入短信服务ISmsService , 用户管理服务UserManager , 验证码Token缓存管理服务SmsCaptchaTokenCache
public class SmsCaptchaManager : DomainService, ICaptchaManager{private readonly ISmsService SmsService;private readonly UserManager _userManager;private readonly SmsCaptchaTokenCache captchaTokenCache;public static TimeSpan TokenCacheDuration = TimeSpan.FromMinutes(5);public SmsCaptchaManager(ISmsService SmsService,UserManager userManager,SmsCaptchaTokenCache captchaTokenCache){this.SmsService=SmsService;_userManager=userManager;this.captchaTokenCache=captchaTokenCache;}}新建SendCaptchaAsync方法 , 作为短信发送和缓存Token方法 , CommonHelp中的GetRandomCaptchaNumber()用于生成随机6位验证码 , 发送完毕后 , 将此验证码作为缓存条目的Key值存入
public async Task SendCaptchaAsync(long userId, string phoneNumber, string purpose){var captcha = CommonHelper.GetRandomCaptchaNumber();var model = new SendSmsRequest();model.PhoneNumbers= phoneNumber;model.SignName="MatoApp";model.TemplateCode= purpose switch{CaptchaPurpose.BIND_PHONENUMBER => "SMS_255330989",CaptchaPurpose.UNBIND_PHONENUMBER => "SMS_255330923",CaptchaPurpose.LOGIN => "SMS_255330901",CaptchaPurpose.IDENTITY_VERIFICATION => "SMS_255330974"};model.TemplateParam= JsonConvert.SerializeObject(new { code = captcha });var result = await SmsService.SendSmsAsync(model);if (string.IsNullOrEmpty(result.BizId) && result.Code!="OK"){throw new UserFriendlyException("验证码发送失败 , 错误信息:"+result.Message);}await captchaTokenCache.SetAsync(captcha, new SmsCaptchaTokenCacheItem(){PhoneNumber=phoneNumber,UserId=userId,Purpose=purpose}, absoluteExpireTime: DateTimeOffset.Now.Add(TokenCacheDuration));}绑定手机号功能实现
public async Task BindAsync(string token){SmsCaptchaTokenCacheItem currentItem = await GetToken(token);if (currentItem==null || currentItem.Purpose!=CaptchaPurpose.BIND_PHONENUMBER){throw new UserFriendlyException("验证码不正确或已过期");}var user = await _userManager.GetUserByIdAsync(currentItem.UserId);if (user.IsPhoneNumberConfirmed){throw new UserFriendlyException("已绑定手机 , 请先解绑后再绑定");}user.PhoneNumber=currentItem.PhoneNumber;user.IsPhoneNumberConfirmed=true;await _userManager.UpdateAsync(user);await RemoveToken(token);}解绑手机号功能实现
public async Task UnbindAsync(string token){SmsCaptchaTokenCacheItem currentItem = await GetToken(token);if (currentItem==null|| currentItem.Purpose!=CaptchaPurpose.UNBIND_PHONENUMBER){throw new UserFriendlyException("验证码不正确或已过期");}var user = await _userManager.GetUserByIdAsync(currentItem.UserId);user.IsPhoneNumberConfirmed=false;await _userManager.UpdateAsync(user);await RemoveToken(token);}

经验总结扩展阅读