手机号验证码登录的思路

引言

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

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

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

整体流程

大致流程如下:

image-20230212110731298

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

获取验证码接口

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

具体代码:

@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频率限制计时不会重置,所以依然不能发送验证码,可以避免别有用心之人恶意调用接口浪费你的短信服务费用。

效果查看

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

image-20230212112809577

获取验证码

image-20230212112849262

redis缓存情况

image-20230212113155286

短时间内再次发送

image-20230212113101213

多次发送超过今日总上限

image-20230212113303432

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

image-20230212113355885

正常登录成功

image-20230212113557452

总结

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/38932.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

手机验证码登录,账号登录结合

本项目基于腾讯外包框架&#xff08;wei框架&#xff09; 地址&#xff1a;https://github.com/twinh/wei/tree/master/docs/zh-CN#wei HTML页面&#xff1a; 【基于bootstrap前端框架】 头部&#xff1a;<ul id"js-reset-tabs" class"nav tab-underline…

手机号验证登录用例

手机号验证登录 1.手机号验证登录是我们现在比较常见的登录方式&#xff0c;下面分享一下基本的用例的编写。能帮助您是我的荣幸&#xff0c;不喜勿喷&#xff0c;谢谢&#xff01;

手机号验证码登录

登录入口 1.app 正常登录入口 2.app 网页登录&#xff0c;比如分享直播卡片时&#xff0c;进入直播间需要先进行登录 3.pc 登录 一&#xff0c;app常见的登录方式 1.手机号验证码登录 2.用户名密码登录 3.一键登录 二&#xff0c;手机验证码登录示意图 三&#xff0c;流…

chatgpt赋能python:Python代码的快捷键:让编程速度更加快速高效

Python代码的快捷键&#xff1a;让编程速度更加快速高效 Python是一种面向对象、直译式计算机程序设计语言&#xff0c;备受程序员喜爱。虽然Python本身已经趋于简洁易读&#xff0c;但使用Python代码编辑器的快捷键可以进一步提高编程效率&#xff0c;帮助合理利用时间。 为…

chatgpt赋能python:Python实现滚动小球

Python实现滚动小球 Python是一种高级编程语言&#xff0c;可以用于开发各种类型的应用程序。在此我们将介绍如何使用Python编写代码实现滚动小球。这个小球会在屏幕上滚动&#xff0c;给用户一个视觉效果。 实现滚动小球的Python代码 import pygameBLACK (0, 0, 0) WHITE …

流浪地球2的科学幻想与现实中的未来计算机科技

作为一个科幻迷、《三体》迷&#xff0c;从小时候第一次看《珊瑚岛上的死光》开始&#xff0c;一直期待一部国产科幻电影。等待了40余年&#xff0c;有生之年终于如愿以偿。 周末二刷了《流浪地球2》。丁老师认为这部科幻电影已经超越了1&#xff0c;以及原著&#xff0c;成为…

还在为投稿发愁吗?ICCVIT 2023,一个计算机、视觉与智能技术国际会议

◆ChatGPT热潮带来的行业革新大行其道&#xff0c;相关区块链、信息安全、视觉、机器学习....产业必将带来诸多变革。 本文为大家梳理了近期可投的4则EI检索的国际会议&#xff0c;均可推荐发表SCI&#xff0c;还有超多IEEE Fellow主讲&#xff0c;不仅涵盖计算机各个学科方向&…

不刷卡,不扫码,微信推出刷掌支付;Meta将再裁员6000人;AI“黑箱”被打开?谷歌找到大模型能力涌现机制|极客头条...

「极客头条」—— 技术人员的新闻圈&#xff01; CSDN 的读者朋友们早上好哇&#xff0c;「极客头条」来啦&#xff0c;快来看今天都有哪些值得我们技术人关注的重要新闻吧。 整理 | 梦依丹 出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09; 一分钟速览新闻点&#…

Chat-GPT有感

Chat-GPT有感 1、Chat-GPT是什么&#xff1f; ​ 2023年当红炸子鸡非Chat-GPT莫属&#xff0c;或许我们即将站在历史的开端、站在人工智能涌现的开端、站在科技百花齐放的开端&#xff0c;我也想问问Chat-GPT&#xff0c;它到底是什么呢&#xff1f; GPT全称为Generative Pre…

前端多语言开发,如何中英文切换

本文以vue为例&#xff1a; 在vue项目中配置两个js文件&#xff0c;一个中文一个英文 将main.js中引入配置好的文件 添加切换中英文的按钮 在methods中写入点击事件 js文件中&#xff0c;把需要的中英文分别写入 在需要的页面中写入 但需要注意的是&#xff0c;在标签中使用$t…

【TypeScript入门】了解TypeScript的工作流程

走进“前端的未来”TS——了解TS的工作流程 文章简介主要内容1 TypeScript工作流程1.1 图示简单解释1.2 理顺区分JS与其他语言 2 如何用TypeScript运行web2.1 为项目添加html文件2.2 用npm高效管理前端项目2.3 npm项目依赖 summary下期预告【基础入门】《前端的未来——TypeScr…

vue2实现中英文切换

简单的使用&#xff08;应用场景&#xff1a;少量几个页面的中英文切换&#xff09; 1、安装 vue-i18n依赖 yarn add vue-i18n 或者 npm install vue-i18n --save-dev 2、在src/components下新建文件夹language&#xff0c;并在文件夹language下新建zh.js及en.js 【src/compon…

vue实现中英文切换

简单的使用&#xff08;应用场景&#xff1a;15个以内页面的中英文切换&#xff09; 1、安装 vue-i18n依赖 yarn add vue-i18n 或者 npm install vue-i18n --save-dev 2、在src/components下新建文件夹language&#xff0c;并在文件夹language下新建zh.js及en.js 【src/co…

vue切换中英语言制作方法(Element+i18n的使用)

介绍&#xff1a; Vue I18n 是 Vue.js 的国际化插件。它可以轻松地将一些本地化功能集成到你的 Vue.js 应用程序中。 1、安装Element ui npm i element-ui -S2、全局引用Element ui 在 main.js 中写入以下内容&#xff1a; import Vue from vue; import ElementUI from elem…

vue3+antd 实现国际化 切换中英文

首先新建结构文件夹 1.下载i18n插件 npm install vue-i18n --save 2.配置i18n.js文件 import { createI18n} from vue-i18n import message from ./langs const localeData {legacy: true, // composition APIlocale: cn,messages: message, } const i18n createI18n(loca…

Vue项目切换语言,vue项目国际化,使用vue-i18n前端实现中英文切换

在项目需求中&#xff0c;我们会遇到国际化的中英文切换。 使用vue搭建的项目中我们可以使用i18n插件进行项目的中英文切换。 npm引入i18n npm install i18n --save-dev为了数据的使用方便&#xff0c;我们需要在src文件夹下建立一个如下图的文件夹 index.js&#xff1a; im…

【latex】论文或参考文献中出现俄文\西班牙文人名的解决方法

常用的latex的编辑器有&#xff1a;texstudio或者overleaf.值得说的是overleaf真的好用&#xff0c;因为它容错率高&#xff0c;即使你的编码语法有些小错误&#xff0c;它也能成功编译。缺点就是&#xff0c;某些论文投稿网站不支持overleaf.但它还是值得我喜欢&#xff0c;因…

前端多语言的切换

前端开发中多语言的切换&#xff0c;可使用js动态替换内容 1、在用户点击切换语言后&#xff0c;把选择的语言版本保存在cookie中 //写入cookie函数 function setCookie(name,value) {var Days 30;var exp new Date();exp.setTime(exp.getTime() Days*24*60*60*1000);docume…

chatgpt赋能python:Python动画制作指南:从入门到精通

Python动画制作指南&#xff1a;从入门到精通 Python作为一种易学易用的编程语言&#xff0c;在数据分析、机器学习等领域已经得到广泛应用。但是你知道吗&#xff1f;Python还可以用来制作动画&#xff01;本文将为你介绍如何用Python制作动画&#xff0c;从入门到精通&#…

吴恩达和OpenAI的《面向开发者的ChatGPT提示工程》精华笔记

《ChatGPT Prompt Engineering for Developers》 面向开发者的ChatGPT提示工程 shadow 趁着假期&#xff0c;学习了prompt课程&#xff0c;做了一些精简和关键知识点的梳理&#xff0c;分享给大家。 LLM 可完成的任务 包括: 总结&#xff08;如总结用户评论&#xff09; 推断&a…