引言

当前很多web端的应用登录方式主要分为以下几种:

  • 账号密码登录
  • 手机号验证码登录
  • 扫码登录

这里我主要说一下我对于手机号验证码登录的思路,如果有遗漏或者差错的地方,请指正;

整体流程

大致流程如下:

大致就是这样,其中一些细节并没有体现出来,下面我用代码演示具体操作步骤;

获取验证码接口

用户输入手机号首先肯定是要获取验证码,所以先要实现获取验证码接口;

具体代码:

@PostMapping("/tencent/code/{phone}")
public BaseResponse<String> tencentSendMessageToPhone(@PathVariable String phone) {// 校验信息this.verifyPhoneInfo(phone);// 如果redis没有该手机号验证码,则获取验证码并发送短信String verifyCode = RandomSmsNumUtils.getSixBitRandom(); // 获取六位验证码Boolean isSend = smsService.tencentSendMessageToPhone(verifyCode, phone); // 调用tencent短信发送sdk// 判断发送结果并处理this.afterMessageSending(isSend, phone, verifyCode);return ResultUtils.success("短信发送成功");
}/*** 校验发送验证码手机号信息* @param phone 手机号*/private void verifyPhoneInfo(String phone) {if (StringUtils.isAnyBlank(phone)) {throw new BusinessException(StatusCode.NULL_ERROR, "手机号为空");}// 校验手机号RegExpUtil.regExpVerify(RegExpUtil.phoneRegExp, phone, "手机号格式错误");// 判断短时间内是否重复发送验证码(限制发送频率60S,60s内只同一个手机号能发送1次)// 从redis中查看有没有该手机号的验证码String verifyCode = (String) redisTemplate.opsForValue().get(RedisKey.SMS_LOGIN_CODE + phone);if (!StringUtils.isAnyBlank(verifyCode)) {long codeCreateTime = Long.parseLong(verifyCode.split("_")[1]); // 获取验证码创建时间if (System.currentTimeMillis() - codeCreateTime < 60000) { // 如果发送间隔小于60秒,则禁止再次发送throw new BusinessException(StatusCode.SMS_CODE_ERROR, "短信发送频繁,请稍后操作");}}// 判断今日发送验证码总次数(限制发送次数10次,24h内只同一个手机号能发送10次)// 该手机号短信发送次数+1long count = redisTemplate.opsForValue().increment(RedisKey.SMS_LIMIT_NUM + phone, 1);if (count == 1) { // count发送次数为一说明该手机号用户今天第一次调用接口发送短信,则开始24小时倒计时redisTemplate.expire(RedisKey.SMS_LIMIT_NUM + phone, 1, TimeUnit.DAYS);}if (count > 10) { // count发送次数大于10次则该手机号超出今日短信发送上线,禁止发送throw new BusinessException(StatusCode.SMS_CODE_ERROR, "今日短信发送次数超出上线");}}/*** 判断发送结果并处理* @param isSend 发送结果* @param phone 发送手机号* @param verifyCode 验证码*/private void afterMessageSending(Boolean isSend, String phone, String verifyCode) {if (isSend) {// 如果发送成功,则将对应手机号验证码存入redis中,设置规定时间内有效redisTemplate.opsForValue().set(RedisKey.SMS_LOGIN_CODE + phone,verifyCode + "_" + System.currentTimeMillis(), // 验证码后加上该验证码创建时间,用来判断该验证码是否是60s内发送的MESSAGE_EXPIRED_TIME,TimeUnit.MINUTES);} else {throw new BusinessException(StatusCode.SYSTEM_ERROR, "短信发送失败");}}

这里需要用到两个redis的key,一个是存验证码+验证码创建时间,用来判断验证码正确性和限制短时间内的发送频率;另一个是存该手机号今日的发送次数,用来限制一天内该手机号发送次数的。

service层调用腾讯云sdk的代码就不演示了,具体的可以看这篇文章:腾讯云短信服务——获取验证码

获取验证码接口就完成了;

手机号验证码登录接口

用户输入验证码后点击登录所调用的接口,思路比较简单,代码如下:

controller

// 手机号验证码登录接口
@PostMapping("/phone")
public BaseResponse<LoginVo> phoneCodeLogin(@RequestBody PhoneCodeLoginRequest loginRequest) {if (loginRequest == null || StringUtils.isAnyBlank(loginRequest.getPhone(), loginRequest.getCode())) {throw new BusinessException(StatusCode.PARAMS_ERROR);}String phone = loginRequest.getPhone();String code = loginRequest.getCode();LoginVo loginVo = userService.phoneCodeLogin(phone, code);return ResultUtils.success(loginVo);
}

serviceImpl

@Override
@Transactional
public LoginVo phoneCodeLogin(String phone, String code) {// 参数校验if (StringUtils.isAnyBlank(phone, code)) {throw new BusinessException(StatusCode.PARAMS_ERROR, "参数为空");}RegExpUtil.regExpVerify(RegExpUtil.phoneRegExp, phone, "手机号格式错误");// 从redis中获取验证码进行校验String phoneCode = (String) redisTemplate.opsForValue().get(RedisKey.SMS_LOGIN_CODE + phone);if (StringUtils.isAnyBlank(phoneCode)) {throw new BusinessException(StatusCode.OPERATION_ERROR, "验证码不存在或已超时");}phoneCode = phoneCode.split("_")[0]; // 获取真正的验证码if (!code.equals(phoneCode)) {throw new BusinessException(StatusCode.OPERATION_ERROR, "验证码错误");}// 判断该用户是否存在,若不存在,则注册为新用户User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getPhone, phone));if (user == null) { // 注册为新用户user = new User();user.setPhone(phone);user.setAvatar(ApiUtils.getRandomAvatar());user.setProfile("简单介绍一下自己吧!");user.setNickname(RandomNameUtils.randomName(true, 3));userMapper.insert(user);// 重新获取新增用户信息user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getPhone, phone));// 设置用户为普通用户Role normalRole = roleService.getOne(new LambdaQueryWrapper<Role>().eq(Role::getRoleName, "普通用户"));UserRoleRelation userRoleRelation = new UserRoleRelation();userRoleRelation.setUserId(user.getId());userRoleRelation.setRoleId(normalRole.getId());userRoleRelationService.save(userRoleRelation);}// 用户存在(或已注册成功)进行登录操作// 封装用户信息UserVo userVo = setUserVo(user);// 生成tokenString token = JwtUtil.createJWT(user.getId().toString());// 将用户信息存入redisredisTemplate.opsForValue().set(RedisKey.LOGIN_USER + user.getId(), userVo, 14, TimeUnit.DAYS);// 登录成功后将验证码清除redisTemplate.delete(RedisKey.SMS_LOGIN_CODE + phone);// 返回信息return new LoginVo(userVo, token);
}

代码并不难,登录流程清楚了就能看懂了;

前端代码

这里主要想说前端如何做验证码倒计时,我简单用js实现了一下,通过setTimeout进行递归调用即可。

countDown(time) {if (this.codeLoading === false) { // 如果不需要倒计时加载时,time赋为0time = 0}console.log(time)if (time === 0 ) {this.codeContext = '重新获取'this.codeLoading = false} else {this.codeContext = time + '秒后重新获取'time--setTimeout(() => { // 递归countDown函数倒计时this.countDown(time)}, 1000)}
},
// 获取手机号登录验证码
getPhoneCode() {if (!this.phoneLoginForm.phone) {this.$message.warning('请输入手机号')}else {// 发送获取验证码请求getPhoneCodeByAliyun(this.phoneLoginForm.phone).then(res => {if (res.code === 20000) {this.$message.success('验证码发送成功')let time = 60this.codeLoading = true // 禁止点击发送验证码按钮this.countDown(time) // 开始倒计时} else {this.$message.error(res.description)}}).catch(res => {this.$message.error(res.message)})}
},

这样就能实现获取验证码按钮倒计时了,但是当按钮倒计时时刷新界面后倒计时会重置,网上思路是可以把倒计时存到cookie中,但是如果删除cookie还是会重置;但是虽然重置了按钮,但是后端的60s频率限制计时不会重置,所以依然不能发送验证码,可以避免别有用心之人恶意调用接口浪费你的短信服务费用。

效果查看

最后查看一下总体效果吧;

获取验证码

redis缓存情况

短时间内再次发送

多次发送超过今日总上限

对应的redis缓存,超出今日的10次

正常登录成功

总结

我在实现验证码登录过程中感觉有些复杂的地方是验证码的发送频率限制,两次限制的实现方式;其他就没有那么难了;当然我的思路可能会存在一些问题,如果有希望指点一二;

手机号验证码登录的思路相关推荐

  1. 小程序手机号验证码登录

    这是我最近的一个项目--微信小程序 实现手机号- -验证码登录,涉及到了提交参数时需要加密.加密所采用的方式也是按照要求,严格采用16位小加密的MD5的加密形式. login.wxml <vie ...

  2. 测试案例:登录--手机号验证码登录

    手机号验证码登录测试点 1.输入手机号码,待收到手机验证码后,输入验证码 ,成功 2.输入手机号码,待收到手机验证码后,输入错误的验证码,提示:失败 3.输入手机号码,待收到手机验证码后,更改原来手机 ...

  3. 阿里云账户使用手机号+验证码登录开启方法、

    阿里云账号登录支持手机号验证码登录方式,但是如果是2022.4.1前注册的账号,需要先在账号中心开启手机号登录,才可以使用手机号登录.阿里云百科来详细说下阿里云账号开启手机号登录的方法: 阿里云账号手 ...

  4. 前端Vue实现手机号验证码登录(60s禁用倒计时)

    这篇文章主要介绍了Vue实现手机号验证码登录(60s禁用倒计时),帮助大家更好的理解和使用vue,具有很好的参考价值,感兴趣的朋友可以了解下 最近做了个vue项目,前端通过手机号验证码的方式登录,获取 ...

  5. spring oauth2 实现用户名密码登录、手机号验证码登录返回token

    文章目录 介绍 实现功能 用户名密码登录 步骤 编写成功处理器 配置成功处理器 手机号验证码登录 步骤 重写SmsCodeAuthenticationSecurityConfig 测试 用户名密码登录 ...

  6. 短信验证码登录流程思路及详细步骤

    点击蓝色"java大数据修炼之道"关注我哟加个"星标",每晚21:00,一起学技术 来源: blog.csdn.net/classabcd/article/de ...

  7. keycloak单点登录(浙政钉2.0扫码、手机号验证码登录)

    写在前面:本篇博客只针对前端代码实现,keycloak配置什么的,自己和后端或者运维联调吧.说实在的,因为不熟悉keycloak代码的逻辑,再加上时间紧,所以搞了一些很多骚操作. 登录这些前端代码是写 ...

  8. Uni-app 手机号+验证码登录 用户名密码登录登出

    用户名密码登录,手机号登录 用户名密码登录:用了uniapp+uview的$u.debounce防抖方法,再发送请求 判断正则以及同意条款勾选后,发起登录请求(参数是用户名和密码),如果请求成功--s ...

  9. django实现腾讯云短信sdk和redis缓存服务,手机号验证码登录,未注册直接注册登录

    先注册并完成腾讯云短信服务的审核,包括签名和模板:安装redis服务: 1.安装必要库 pip install --upgrade tencentcloud-sdk-python 如果同时有pytho ...

最新文章

  1. jQuery--AJAX传递xml
  2. 《使女的故事》大火,AI是背后最大推手?
  3. 《图解性能优化》笔记
  4. cassandra hbase_为什么选择Cassandra
  5. SQL Server 2014 安装小记
  6. 关于sql中去换行符的问题
  7. sdut 2152:Balloons(第一届山东省省赛原题,DFS搜索)
  8. oracle打开scott用户_Oracle 11gR2中启动Scott用户的方法(推荐)
  9. Stream Part.7
  10. 线性结构 —— ST 表与 RMQ
  11. java list 改变变量的值_3、list改变指针还是改变值
  12. python写excel
  13. 死锁示例代码_Java示例中的死锁
  14. 程序设计基础是C语言吗,程序设计基础(C语言)
  15. 学生学籍管理系统可行性分析报告
  16. solaris系统的crontab定时任务
  17. Java之约瑟夫环问题
  18. emmm最近写了 那个图论入门 找最短路径 Prim 适合比较稠密的图 Kru 适合边比较少的图
  19. ebcdic编码与ascII编码互转
  20. 水泥cement或英语caement水泥

热门文章

  1. 拳王虚拟项目公社:抖音偏门暴利虚拟资源项目分享,抖音壁纸号年赚20W
  2. Matlab 官网查询函数
  3. CSDN改变文章字体颜色,字体大小,字体型号,字体居中、居右、颜文字
  4. Ripple数据本地存储概览
  5. STEMA (STEM 能力测试) 考 试 白 皮 书--scratch编程部分例子试题及实现
  6. Box Model,边距折叠,内联和块标签,CSSReset
  7. 1997考研阅读Text2翻译
  8. Linux NTP服务详解
  9. Python判断字符串是否全是数字或者字母
  10. Gitflow有害论