java如何实现短信验证码登录功能
小编给大家分享一下java如何实现短信验证码登录功能,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!
业务案例
如下所示,是一个大家熟知的采用短信登录的入口
输入手机号之后,出现如下效果,
输入手机上面收到的验证码之后,就可以正常登录了
业务关键点剖析
以上是一个正常的使用短信验证码登录的业务流程,在实际开发中,需要考虑的因素更多了,比如:
验证码位数如何
验证码如何存储
如何预防短信被刷
倒计时功能,前后端如何配合
其实来说,短信验证码功能并不难,难得是如何做到业务场景的全面覆盖和功能细节上面的考虑
短信验证码功能实现思路
小编结合实际经验和调研,目前比较流行的做法是,使用redis做短信验证码,想必说到这里,懂行的同学们应该猜到了
完整的业务逻辑大概如下:
依据这个业务逻辑的实现思路,我们大致可以理清代码的编写逻辑,在小编开发过程中,其中有一个点遇到了一点梗,就是关于验证码的有效期的问题,主要考虑下面2点:
后端存储验证码有效期时长
前端页面倒计时和后端有效期的关系
有效期问题
下面我们编写代码来演示下完整的过程
前置准备:搭建一个springboot工程
操作步骤
1、导入核心依赖
org.springframework.boot spring-boot-starter-data-redis org.springframework.boot spring-boot-starter-web 2.2.1.RELEASE
2、编写获取短信验证码方法
@Servicepublic class SmsServiceImpl implements SmsService { public static final String VERIFY_CODE = "login:verify_code:"; @Autowired private DbUserMapper dbUserMapper; @Autowired private RedisTemplateredisTemplate; @Override public String getSmsVerifyCode(String phone) { if (StringUtils.isEmpty(phone)) { throw new RuntimeException("用户手机号为空"); } QueryWrapper queryWrapper = new QueryWrapper(); queryWrapper.eq("mobile",phone); DbUser dbUser = dbUserMapper.selectOne(queryWrapper); if(dbUser == null){ throw new RuntimeException("用户不存在"); } String smsVerifyCode = getSmsVerifyCode(); String smsCodeKey = VERIFY_CODE + dbUser.getUserId(); String existedSmsCode = redisTemplate.opsForValue().get(smsCodeKey); //如果验证码已经存在时 if (StringUtils.isNotEmpty(existedSmsCode)) { Long expireTime = redisTemplate.opsForValue().getOperations().getExpire(smsCodeKey); long lastTime = 60 * 3 - expireTime; //三分钟内验证码有效,1分钟到3分钟之间,用户可以继续输入验证码,也可以重新获取验证码,新的验证码将覆盖旧的 if(lastTime > 60 && expireTime >0){ //调用第三方平台发短信,只有短信发送成功了,才能将短信验证码保存到redis System.out.println("此处调用短信发送逻辑......"); redisTemplate.opsForValue().set(smsCodeKey, smsVerifyCode, 60 * 3, TimeUnit.SECONDS); System.out.println("短信验证码:" + smsVerifyCode); } //一分钟之内不得多次获取验证码 if(lastTime < 60){ throw new RuntimeException("操作过于频繁,请一分钟之后再次点击发送"); } }else { //调用notify服务发送短信,只有notify的短信发送成功了,才能将短信验证码保存到redis System.out.println("此处调用短信发送逻辑......"); System.out.println("短信验证码:" + smsVerifyCode); redisTemplate.opsForValue().set(smsCodeKey, smsVerifyCode, 60 * 3, TimeUnit.SECONDS); } return smsVerifyCode; } /** * 随机获取6位短信数字验证码 * * @return */ public static String getSmsVerifyCode() { Random random = new Random(); String code = ""; for (int i = 0; i < 6; i++) { int rand = random.nextInt(10); code += rand; } return code; }}
发送短信验证码需充分考虑几个场景:
首次输入手机号,获取验证码时,后端设置验证码有效期为3分钟,前端倒计时1分钟
1分钟之内,用户不能第二次获取验证码,1分钟之后,用户可以重新获取验证码
超过1分钟后,同一个用户再次点击获取验证码时,后端需主动移除redis存储的上一次验证码
3分钟有效期内,用户可随时输入第一次的验证码进行登录
登录成功后,登录接口需主动移除redis中的验证码
以上为验证码的核心业务方法,下面再编写一个登录的方法,当基础校验和验证码校验通过后,即可登录
@Override public String login(String userId, String smsCode) { if (StringUtils.isEmpty(userId)) { throw new RuntimeException("用户ID必传"); } if (StringUtils.isEmpty(smsCode)) { throw new RuntimeException("验证码不能为空"); } QueryWrapper queryWrapper = new QueryWrapper(); queryWrapper.eq("user_id",userId); DbUser dbUser = dbUserMapper.selectOne(queryWrapper); if(dbUser == null){ throw new RuntimeException("用户不存在"); } //校验验证码 String smsCodeKey = VERIFY_CODE + dbUser.getUserId(); String verifyCode = redisTemplate.opsForValue().get(smsCodeKey); if (StringUtils.isEmpty(verifyCode)) { throw new RuntimeException("短信验证码不存在或已过期"); } if (!StringUtils.equals(smsCode, verifyCode)) { throw new RuntimeException("短信验证码错误"); } //TODO 其他待验证的登录业务逻辑 System.out.println("执行其他业务......"); System.out.println("登录成功"); //最后清理下验证码 if(redisTemplate.hasKey(smsCodeKey)){ redisTemplate.delete(smsCodeKey); } return "login success"; }
3、提供获取验证码和登录接口
@RestControllerpublic class SmsController { @Autowired private SmsService smsService; @GetMapping("get/sms_code") public String getSmsVerifyCode(@RequestParam("phone") String phone){ return smsService.getSmsVerifyCode(phone); } /** * 登录 * @param userId * @param smsCode * @return */ @GetMapping("/login") public String login(@RequestParam("userId") String userId,@RequestParam("smsCode") String smsCode){ return smsService.login(userId,smsCode); }}
启动redis服务,启动项目,数据库提前准备一条数据
场景测试1:获取验证码
调用登录接口,使用上面的验证码:
场景测试2:1分钟内多次获取验证码
第一次获取验证码
再次获取验证码
超过1分钟少于3分钟内,再次获取验证码,得到新的验证码,同时redis中存储的是最新的验证码
场景测试3:登录输入错误验证码
总体来说,使用redis实现短信验证码登录的功能不算太复杂,主要是需要全面的考虑到各自使用场景即可
以上是"java如何实现短信验证码登录功能"这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注行业资讯频道!