IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> java短信验证码登功能设计与实现 -> 正文阅读

[Java知识库]java短信验证码登功能设计与实现

前言

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

业务案例

如下所示,是一个大家熟知的采用短信登录的入口
在这里插入图片描述

输入手机号之后,出现如下效果,
在这里插入图片描述
输入手机上面收到的验证码之后,就可以正常登录了

业务关键点剖析

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

  • 验证码位数如何
  • 验证码如何存储
  • 如何预防短信被刷
  • 倒计时功能,前后端如何配合

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

短信验证码功能实现思路

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

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

  1. 后端存储验证码有效期时长
  2. 前端页面倒计时和后端有效期的关系

有效期问题

在这里插入图片描述
在这里插入图片描述

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

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

操作步骤

1、导入核心依赖

		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        
		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.2.1.RELEASE</version>
        </dependency>

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

@Service
public class SmsServiceImpl implements SmsService {

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

    @Autowired
    private DbUserMapper dbUserMapper;

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    @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实现短信验证码登录的功能不算太复杂,主要是需要全面的考虑到各自使用场景即可,本篇到此结束,最后感谢观看!

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-11-25 07:59:26  更:2021-11-25 07:59:52 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 3:30:39-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码