基于SpringBoot和Redis实现短信验证码功能

基于SpringBoot和Redis实现短信验证码功能

app中使用短信登录,使用redis实现

(一)准备工作

1.1引入相关依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <exclusions>
        <exclusion>
            <artifactId>spring-data-redis</artifactId>
            <groupId>org.springframework.data</groupId>
        </exclusion>
        <exclusion>
            <artifactId>lettuce-core</artifactId>
            <groupId>io.lettuce</groupId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-redis</artifactId>
    <version>2.6.2</version>
</dependency>
<dependency>
    <groupId>io.lettuce</groupId>
    <artifactId>lettuce-core</artifactId>
    <version>6.1.6.RELEASE</version>
</dependency>

1.2在application.yaml文件中配置相关数据

redis:
  host: 192.168.***.***
  port: 6379
  password: 123456
  lettuce:
    pool:
      max-active: 10
      max-idle: 10
      min-idle: 1
      time-between-eviction-runs: 10s

(二)短信的发送

2.1思路

用户从前端发送手机号到后台服务器,首先需要经过service层验证手机号格式是否正确,将手机号传给某个接口(市面上很多),验证通过,发送验证码。然后service层随机生成一个6位数的随机字符串。之后首先将生成的验证码保存在redis中并设置有效时间(防止redis内存占满的情况),(这里采用String数据结构保存验证码,并且以手机号作为key)。然后向用户提供的手机号上发送本次随机生成的验证码。

2.2代码编写

@Override
public Result sendCode(String phone, HttpSession session) {
    //1校验手机号
    if(RegexUtils.isPhoneInvalid(phone)){
        //2如果不符合,返回错误信息
        return Result.fail("手机号格式错误");
    }
    //3符合生成验证码
    String code = RandomUtil.randomNumbers(6);
    //4保存验证码到redis,并设置有效期(防止内存占满setex)
    stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY + phone,code,LOGIN_CODE_TTL, TimeUnit.MINUTES);
    //5发送验证码(本业务的功能在于学习redis,发送验证码需要整合第三方接口,这里假装发送一下)
    log.debug("发送验证码成功,验证码为{}",code);
    //返回🆗
    return Result.ok();
}

(三)用户登录功能

3.1思路

用户再次提交手机号和验证码,此时再次验证手机号格式是否正确,如果格式正确则去redis里查找该手机号对应的验证码,并与用户提供的验证码对比,如果两个验证码相同,则从数据库中查找到该user,并把用户信息也保存在redis中并设置有效时间以便后续功能的使用。(这里选择hash数据结构存储用户数据,因为考虑到用户的信息时一个个的对象)

3.2代码编写

@Override
public Result login(LoginFormDTO loginForm, HttpSession session) {
    //用户提交过来手机号和验证码,对手机号和验证码进行校验
    // 1.1校验手机号
    String phone = loginForm.getPhone();
    if(RegexUtils.isPhoneInvalid(phone)){
        //2如果不符合,返回错误信息
        return Result.fail("手机号格式错误");
    }
    // 1.2校验验证码(从redis里获取)
    String code = loginForm.getCode();//用户提交的验证码
    String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);
    // 2不一致,报错
    if(cacheCode == null || !cacheCode.toString().equals(code)){
        return Result.fail("验证码错误");
    }

    // 3一致,根据手机号查询用户
    User user = query().eq("phone", phone).one();
    // 4判断用户是否存在
    // 5不存在,创建新用户
    if (user == null) {
        //创建新用户,并且保存
        user = createUserWithPhone(phone);
    }
    // 6存在,保存用户信息到redis(采用hash作为存储数据结构,用一个随机生成的token作为key,不用手机号是因为最终的token要保存在浏览器,使用手机号的话不安全)

    String token = UUID.randomUUID().toString(true);
    // 6.1将用户对象转为hashmap存储
    UserDTO userDTO = BeanUtil.copyProperties(user,UserDTO.class);
    Map<String, Object> userMap = BeanUtil.beanToMap(userDTO,new HashMap<>(),
            CopyOptions.create()
                .setIgnoreNullValue(true)
                .setFieldValueEditor((filedName,filedValue) -> filedValue.toString()));
    //6.2存储到redis并设置有效时间(30min),并且当用户操作浏览器时更新有效期
    stringRedisTemplate.opsForHash().putAll(LOGIN_USER_KEY + token,userMap);
    stringRedisTemplate.expire(LOGIN_USER_KEY + token,LOGIN_USER_TTL,TimeUnit.MINUTES);
    // 6.3通过登录拦截校验
    return Result.ok(token);
}

(四)通过拦截器更新用户信息的有效时间

4.1必要性

由于登录后设置了用户保存在redis中的有效时间为30min,当30min过后,如果用户还在使用该app而信息过期,那么用户还需要再次登录,体验不好,应该是当用户在操作该app时去更新有效时间。保证用户使用时user信息永远不会过期。如何知道用户是否在操作app?可以在拦截器中对用户请求进行拦截,每当用户发送请求时,都会经过拦截器,所以可以在拦截器中更新有效时间

4.2代码编写

拦截器
public class LoginInterceptor implements HandlerInterceptor {

    private StringRedisTemplate stringRedisTemplate;

    //这个类是我们自己手动写出来的,并没有添加到spring容器中,所以只能用构造放法的方式注入
    public LoginInterceptor(StringRedisTemplate stringRedisTemplate){
        this.stringRedisTemplate = stringRedisTemplate;
    }
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 1获取请求头中的token,判断是否登录
        String token = request.getHeader("authorization");
        if (StrUtil.isBlank(token)) {
            response.setStatus(401);
            return false;
        }
        // 2基于token获取redis中的用户
        Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(RedisConstants.LOGIN_USER_KEY + token);
        // 3判断用户是否存在,主要判断登录是否过期
        if (userMap.isEmpty()) {
            // 4不存在,拦截,返回401
            response.setStatus(401);
            return false;
        }

        // 5将查询到的Hash数据转换为UserDTO对象
        UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);

        // 6存在,保存用户信息到ThreadLocal
        UserHolder.saveUser(userDTO);

        // 7刷新token有效期
        stringRedisTemplate.expire(RedisConstants.LOGIN_USER_KEY + token,RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);
        // 8放行

        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        UserHolder.removeUser();
    }
}
配置拦截器并排除一些请求的拦截
@Configuration
public class MvcConfig implements WebMvcConfigurer {

    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor(stringRedisTemplate)).excludePathPatterns(
                "/user/code",
                "/user/login",
                "/shop/**",
                "/blog/hot",
                "/upload/**",
                "/shop-type/**",
                "/voucher/**"
        );
    }
}

(五)总结

短信登录功能主要用到redis里的几种数据结构,其中String类型用于存储服务器生成的验证码,Hash用于存储用户的基本信息。这里存储的不需要是用户的完整信息,可以通过声明一个类,里面只有用户的最重要的信息就行。还需要设置这些键值对的过期时间,防止redis内存被占满的情况。还需要通过拦截器拦截用户的请求,以便更新用户信息的有效时间,防止用户在操作过程中信息过期。

  • 11
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
【毕业设计】基于springboot的仿共享单车后台源码及笔记【源码+SQL脚本】.zip 一个基于springboot的类似于共享单车的后台系统,包括手机号码注册、登陆、定位、骑行轨迹和消息推送等功能 技术栈: SpringBoot SpringMVC Mybatis(基础框架) Mysql springSecurity(权限验证和请求拦截) AES对称加密数据,RSA非对称加密公钥key(对用户息进行加密) redis缓存token(token作为用户的标识,维护用户的状态,类似于session) redis结合ActiveMQ发送验证码和防止恶意无限发送 整合云存储,保存头像(七牛云对象存储为例) 整合mongodb获取附近单车以及距离,主要是geoHash算法(重点) 百度云推送的实战-通知开锁 锁车之后的一系列操作(订单、消费、锁车,支付没有做) 骑行轨迹的保存(保存在mongodb中) 其他:logback,lombok,fastjson, 全局异常和高复用状态类封装、 利用mysql主键自增特性实现单车连续编号 学习这个项目的时候,对其思路做了笔记,我这里按照自己的写法,与其略有不同, 但是基本思路都是一样的。 注意: 本机环境是jdk8,tomcat8,mysql5.7.13,windows下,IDE是IntelliJ IDEA, mongodb(mongodb-win32-x86_64-2008plus-v3.2-latest),redis(redis64-3.0.501), activeMq(apache-activemq-5.15.2) api测试工具为Postman,还用了mongodb客户端Robo 3T,redis客户端工具RedisDesktopManage 跑这个代码之前,需要准备好数据库,开启redis、mongodb、activeMQ服务。 需要注册自己的七牛云账号(上传头像)、秒嘀账号(发送验证码)、百度云推送账号
好的,下面是实现步骤: 1. 添加 Redis 依赖 在 `pom.xml` 文件中添加 Redis 依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> ``` 2. 配置 Redis 在 `application.properties` 文件中添加 Redis 相关配置: ```properties # Redis配置 spring.redis.host=127.0.0.1 spring.redis.port=6379 spring.redis.password= spring.redis.database=0 spring.redis.timeout=60000 ``` 3. 编写验证码生成和存储逻辑 编写一个 `VerificationCodeUtil` 工具类,生成验证码并将验证码存储到 Redis 中: ```java @Component public class VerificationCodeUtil { @Autowired private RedisTemplate<String, String> redisTemplate; /** * 生成验证码 * @param key 键 * @param expire 过期时间 * @return 验证码 */ public String generateCode(String key, long expire) { //生成四位数字的验证码 String code = String.format("%04d", new Random().nextInt(9999)); //将验证码存储到 RedisredisTemplate.opsForValue().set(key, code, expire, TimeUnit.SECONDS); return code; } } ``` 4. 编写验证码登录逻辑 编写一个 `LoginController` 控制器,实现验证码登录功能: ```java @RestController public class LoginController { @Autowired private VerificationCodeUtil verificationCodeUtil; @Autowired private RedisTemplate<String, String> redisTemplate; @PostMapping("/login") public String login(String phone, String code) { //从 Redis 中获取验证码 String cacheCode = redisTemplate.opsForValue().get(phone); if (cacheCode == null) { return "验证码已过期,请重新获取"; } if (!cacheCode.equals(code)) { return "验证码错误"; } //验证验证通过,执行登录逻辑 return "登录成功"; } @GetMapping("/getCode") public String getCode(String phone) { //生成验证码并存储到 Redis 中,有效期为60秒 String code = verificationCodeUtil.generateCode(phone, 60); return code; } } ``` 至此,我们已经实现了使用 Redis 存储验证码并实现验证码登录的功能

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值