在一些系统中,有时候用户忘记密码,可以通过向自己手机发送动态验证码的方式实现系统登录功能。本篇随笔介绍如何结合后端ABP框架的短信发送和缓存模块的处理,实现手机短信动态码登陆处理。
一般的登录方式,分为普通账号登录,动态密码登陆,扫描二维码登录等几种方式,其他方式这里不讲,主要介绍动态码登录方式。
1、短信验证码的发送处理
我在上篇随笔《ABP框架中短信发送处理,包括阿里云短信和普通短信商的短信发送集成》中介绍了如何使用ABP框架实现短信的发送处理,因此我们前后端通过短信的方式,可以实现动态密码的登陆处理。
因此在授权登陆的控制器中,我们增加短信发送的接口注入使用,如下所示。
然后通过定义两个接口,一个是发送动态验证码给用户手机的接口,一个是根据用户手机和动态验证码的方式进行登录处理接口。
然后我们在这个验证身份的控制器上增加两个方法即可。
用例也就是分了两个处理方法。
在处理发送短信验证码之前,我们来介绍一下短信验证码的处理规则,我们发送短信成功后,把验证码存在系统缓存里面,一般系统缓存是存放在Redis里面,缓存需要一个键和定义好的类对象进行存储。
我们定义好存储的对象类,再在系统中使用即可。
/// <summary> /// 短信登陆动态密码缓存对象 /// </summary> [Serializable] public class SmsLoginCodeCacheItem { public const string CacheName = "AppSmsLoginCodeCacheItem"; public string Code { get; set; } public string PhoneNumber { get; set; } public SmsLoginCodeCacheItem() { } public SmsLoginCodeCacheItem(string code, string phone) { Code = code; PhoneNumber = phone; } }
我们可以在系统模块初始化的时候,配置好缓存对应的失效时间,如下所示。
//配置SMS登录动态码有效期限 Configuration.Caching.Configure(SmsLoginCodeCacheItem.CacheName, cache => { cache.DefaultSlidingExpireTime = TimeSpan.FromMinutes(Constants.SmsCodeExpiredMinutes); });
发送短信验证码作为动态密码的逻辑代码如下所示。
/// <summary> /// 发送登录动态码 /// </summary> /// <param name="model">手机登录动态码</param> /// <returns></returns> [HttpPost] public async Task<CommonResult> SendPhoneLoginSmsCode([FromBody] AuthenticateByPhoneCaptchaModel model) { //获取随机6位数字动态验证码 var code = RandomHelper.GetRandom(100000, 999999).ToString(); //使用自定义模板处理短信发送 string message = string.Format(Constants.MySmsCodeTemplate, code); var result = await _smsSender.SendAsync(model.PhoneNumber, message); if(result.Success) { var cacheKey = model.PhoneNumber;//以手机号码作为键存储验证码缓存 var cacheItem = new SmsLoginCodeCacheItem { Code = code, PhoneNumber = model.PhoneNumber }; var cache = _cacheManager.GetCache<string, SmsLoginCodeCacheItem>(SmsLoginCodeCacheItem.CacheName); cache.Set(cacheKey, cacheItem); } return result; }
我们还需要在前端中设计一个使用动态短信码登录的界面,如下所示。
短信发送成功,可以在用户手机查看对应的动态码。
验证码发送后,我们也可以在Redis中看到对应的数据,如下所示。
2、动态码登录处理
发送了短信码后,系统在缓存中存放一段时间的数据,如果在这个期间进行登录,会根据缓存进行匹配,如果匹配成功,那么就进行相关登录身份的处理即可。
系统登录验证的处理代码如下所示。
/// <summary> /// 通过手机验证码授权 /// </summary> /// <param name="model">手机验证码Dto</param> /// <returns></returns> [HttpPost] public async Task<AuthenticateResultModel> AuthenticateByPhoneCaptcha([FromBody] AuthenticateByPhoneCaptchaModel model) { var loginResult = await GetLoginResultByPhoneCaptchaAsync( model.PhoneNumber, model.SmsCode, GetTenancyNameOrNull() ); //if(loginResult.Result == AbpLoginResultType.Success) //这里成功,移除短信缓存 var cache = _cacheManager.GetCache<string, SmsLoginCodeCacheItem>(SmsLoginCodeCacheItem.CacheName); cache.Remove(model.PhoneNumber);//移除缓存短信键值 var accessToken = CreateAccessToken(CreateJwtClaims(loginResult.Identity)); return new AuthenticateResultModel { AccessToken = accessToken, ExpireInSeconds = (int)_configuration.Expiration.TotalSeconds, EncryptedAccessToken = GetEncryptedAccessToken(accessToken), UserId = loginResult.User.Id }; }
这里主要的逻辑封装在 GetLoginResultByPhoneCaptchaAsync 中,这个登录的方式可以参考ABP框架基础的登陆方式进行改动即可。
/// <summary> /// 获取登陆结果通过手机验证码 /// </summary> /// <param name="phoneNumber">手机号</param> /// <param name="captcha">验证码</param> /// <param name="tenancyName">租户名</param> /// <returns></returns> private async Task<AbpLoginResult<Tenant, User>> GetLoginResultByPhoneCaptchaAsync(string phoneNumber, string captcha, string tenancyName) { var loginResult = await _logInManager.LoginByMobileAsync(phoneNumber, captcha, tenancyName); switch (loginResult.Result) { case AbpLoginResultType.Success: return loginResult; default: throw _abpLoginResultTypeHelper.CreateExceptionForFailedLoginAttempt(loginResult.Result, loginResult.User.UserName, tenancyName); } }
参照ABP框架基础的登陆授权方式,我们在UserManager中增加类似的验证码登陆管理方式,如下所示。
前端在处理相关发送验证码和登录授权的操作,是针对API的调用,因此需要封装对应的API处理。
然后仿照常规登录的处理,编写一个动态码登录的处理方式,放在对应的Module中即可。
dynamiclogin({ commit }, userInfo) { // 动态密码登陆 const { mobile, smscode } = userInfo return new Promise((resolve, reject) => { tokenauth.AuthenticateByPhoneCaptcha({ phoneNumber: mobile.trim(), smsCode: smscode }).then(response => { const { result } = response // 获取返回对象的 result // console.log(result)// 记录数据 var token = result.accessToken // 用户令牌 var userId = result.userId // 用户id // 修改State对象,记录令牌和用户Id commit('SET_TOKEN', token) commit('SET_USERID', userId) // 存储cookie setToken(token) setUserId(userId) resolve() }).catch(error => { reject(error) }) }) },
在登录界面中,输入动态码登录即可顺利进入系统,和常规的处理一样。
以上就是参照常规账号密码登录的方式,构建一个动态码登录的处理,流程还是差不多,不过整合了短信发送,缓存处理,账号登陆等几个流程,可以作为一个简单的系统登录过程的了解。
为了方便读者理解,我列出一下前面几篇随笔的连接,供参考:
循序渐进VUE+Element 前端应用开发(1)--- 开发环境的准备工作
循序渐进VUE+Element 前端应用开发(2)--- Vuex中的API、Store和View的使用
循序渐进VUE+Element 前端应用开发(3)--- 动态菜单和路由的关联处理
循序渐进VUE+Element 前端应用开发(4)--- 获取后端数据及产品信息页面的处理
循序渐进VUE+Element 前端应用开发(5)--- 表格列表页面的查询,列表展示和字段转义处理
循序渐进VUE+Element 前端应用开发(6)--- 常规Element 界面组件的使用
循序渐进VUE+Element 前端应用开发(7)--- 介绍一些常规的JS处理函数
循序渐进VUE+Element 前端应用开发(8)--- 树列表组件的使用
循序渐进VUE+Element 前端应用开发(9)--- 界面语言国际化的处理
循序渐进VUE+Element 前端应用开发(10)--- 基于vue-echarts处理各种图表展示
循序渐进VUE+Element 前端应用开发(11)--- 图标的维护和使用
循序渐进VUE+Element 前端应用开发(12)--- 整合ABP框架的前端登录处理
循序渐进VUE+Element 前端应用开发(13)--- 前端API接口的封装处理
循序渐进VUE+Element 前端应用开发(14)--- 根据ABP后端接口实现前端界面展示
循序渐进VUE+Element 前端应用开发(15)--- 用户管理模块的处理
循序渐进VUE+Element 前端应用开发(16)--- 组织机构和角色管理模块的处理
循序渐进VUE+Element 前端应用开发(17)--- 菜单管理
循序渐进VUE+Element 前端应用开发(18)--- 功能点管理及权限控制
VUE+Element 前端应用开发框架功能介绍
循序渐进VUE+Element 前端应用开发(19)--- 后端查询接口和Vue前端的整合
使用代码生成工具快速生成基于ABP框架的Vue+Element的前端界面
循序渐进VUE+Element 前端应用开发(20)--- 使用组件封装简化界面代码
循序渐进VUE+Element 前端应用开发(21)--- 省市区县联动处理的组件使用
循序渐进VUE+Element 前端应用开发(22)--- 简化main.js处理代码,抽取过滤器、全局界面函数、组件注册等处理逻辑到不同的文件中
循序渐进VUE+Element 前端应用开发(23)--- 基于ABP实现前后端的附件上传,图片或者附件展示管理
循序渐进VUE+Element 前端应用开发(24)--- 修改密码的前端界面和ABP后端设置处理
循序渐进VUE+Element 前端应用开发(25)--- 各种界面组件的使用(1)
循序渐进VUE+Element 前端应用开发(26)--- 各种界面组件的使用(2)
电商商品数据库的设计和功能界面的处理
循序渐进VUE+Element 前端应用开发(27)--- 数据表的动态表单设计和数据存储
循序渐进VUE+Element 前端应用开发(28)--- 附件内容的管理
循序渐进VUE+Element 前端应用开发(29)--- 高级查询条件的界面设计
部署基于.netcore5.0的ABP框架后台Api服务端,以及使用Nginx部署Vue+Element前端应用
循序渐进VUE+Element 前端应用开发(30)--- ABP后端和Vue+Element前端结合的分页排序处理
循序渐进VUE+Element 前端应用开发(31)--- 系统的日志管理,包括登录日志、接口访问日志、实体变化历史日志
循序渐进VUE+Element 前端应用开发(32)--- 手机短信动态码登陆处理
循序渐进VUE+Element 前端应用开发(33)--- 邮件参数配置和模板邮件发送处理
使用代码生成工具快速开发ABP框架项目
使用Vue-TreeSelect组件实现公司-部门-人员级联下拉列表的处理
使用Vue-TreeSelect组件的时候,用watch变量方式解决弹出编辑对话框界面无法触发更新的问题