java短信验证码登录功能设计与实现

网友投稿 343 2022-11-21

java短信验证码登录功能设计与实现

目录前言业务案例业务关键点剖析短信验证码功能实现思路有效期问题操作步骤

前言

现在不管是各类的网站,还是大小社交app,登录方式是越来越多了,其中基于短信验证码的登录可以说是各类app必不可少的方式,短信验证码登录以其高效,安全,便捷等特性受到许多用户的青睐

业务案例

如下所示,是一个大家熟知的采用短信登录的入口

输入手机号之后,出现如下效果,

输入手机上面收到的验证码之后,就可以正常登录了

业务关键点剖析

以上是一个正常的使用短信验证码登录的业务流程,在实际开发中,需要考虑的因素更多了,比如:

验证码位数如何

验证码如何存储

如何预防短信被刷

倒计时功能,前后端如何配合

其实来说,短信验证码功能并不难,难得是如何做到业务场景的全面覆盖和功能细节上面的考虑

短信验证码功能实现思路

结合实际经验和调研,目前比较流行的做法是,使用redis做短信验证码,想必说到这里,懂行的同学们应该猜到了

完整的业务逻辑大概如下:

依据这个业务逻辑的实现思路,我们大致可以理清代码的编写逻辑,在开发过程中,其中有一个点遇到了一点梗,就是关于验证码的有效期的问题,主要考虑下面2点:

后端存储验证码有效期时长

前端页面倒计时和后端有效期的关系

有效期问题

下面我们编写代码来演示下完整的过程

前置准备:搭建一个springboot工程

操作步骤

1、导入核心依赖

org.springframework.boot

spring-boot-starter-data-redis

org.springframework.boot

spring-boot-starter-web

2.2.1.RELEASE

2、编写获取短信验证码方法

@Service

public class SmsServiceImpl implements SmsService {

public static final String VERIFY_CODE = "login:verify_code:";

@Autowired

private DbUserMapper dbUserMapper;

@Autowired

private RedisTemplate redisTemplatehttp://;

@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、提供获取验证码和登录接口

@RestController

public 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实现短信验证码登录的功能不算太复杂,主要是需要全面的考虑到各自使用场景即可

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:基于信号接口的自动测试系统软件的设计与实现
下一篇:sqoop如何指定pg库的模式
相关文章

 发表评论

暂时没有评论,来抢沙发吧~