Java简单实现短信验证登录(Session、Redis)

前端设计

一、基于Session实现登录

流程

发送验证码:

用户在提交手机号后,会校验手机号是否合法,如果不合法,则要求用户重新输入手机号

如果手机号合法,后台此时生成对应的验证码,同时将验证码进行保存,然后再通过短信的方式将验证码发送给用户

短信验证码登录、注册:

用户将验证码和手机号进行输入,后台从session中拿到当前验证码,然后和用户输入的验证码进行校验,如果不一致,则无法通过校验,如果一致,则后台根据手机号查询用户,如果用户不存在,则为用户创建账号信息,保存到数据库,无论是否存在,都会将用户信息保存到session中,方便后续获得当前登录信息

校验登录状态:

用户在请求时候,会从cookie中携带者JsessionId到后台,后台通过JsessionId从session中拿到用户信息,如果没有session信息,则进行拦截,如果有session信息,则将用户信息保存到threadLocal中,并且放行。

具体实现

正则工具类: pringBoot后端一些常见的正则表达校验

Result类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result { private Boolean success;
    private String errorMsg;
    private Object data;
    private Long total;
    public static Result ok(){ return new Result(true, null, null, null);
    }
    public static Result ok(Object data){ return new Result(true, null, data, null);
    }
    public static Result ok(List data, Long total){ return new Result(true, null, data, total);
    }
    public static Result fail(String errorMsg){ return new Result(false, errorMsg, null, null);
    }
}
  • 发送验证码
     @Override
        public Result sendCode(String phone, HttpSession session) { // 1.校验手机号
            if (RegexUtils.isPhoneInvalid(phone)) { // 2.如果不符合,返回错误信息
                return Result.fail("手机号格式错误!");
            }
            // 3.符合,生成验证码
            String code = RandomUtil.randomNumbers(6);
            // 4.保存验证码到 session
            session.setAttribute("code",code);
            // 5.发送验证码 (下面介绍阿里云SMS服务发送)
            log.debug("发送短信验证码成功,验证码:{}", code);
            // 返回ok
            return Result.ok();
        }
    
    • 登录
      @Override
          public Result login(LoginFormDTO loginForm, HttpSession session) { // 1.校验手机号
              String phone = loginForm.getPhone();
              if (RegexUtils.isPhoneInvalid(phone)) { // 2.如果不符合,返回错误信息
                  return Result.fail("手机号格式错误!");
              }
              // 3.校验验证码
              Object cacheCode = session.getAttribute("code");
              String code = loginForm.getCode();
              if(cacheCode == null || !cacheCode.toString().equals(code)){ //3.不一致,报错
                  return Result.fail("验证码错误");
              }
              //一致,根据手机号查询用户
              User user = query().eq("phone", phone).one();
              //5.判断用户是否存在
              if(user == null){ //不存在,则创建
                  user =  createUserWithPhone(phone);
              }
              //7.保存用户信息到session中
              session.setAttribute("user",user);
              return Result.ok();
          }
      

      二、基于Redis实现短信登录

      流程

      当注册完成后,用户去登录会去校验用户提交的手机号和验证码,是否一致,如果一致,则根据手机号查询用户信息,不存在则新建,最后将用户数据保存到redis,并且生成token作为redis的key,当我们校验用户是否登录时,会去携带着token进行访问,从redis中取出token对应的value,判断是否存在这个数据,如果没有则拦截,如果存在则将其保存到threadLocal中,并且放行。

      实现

      自定义常量

      public class RedisConstants { public static final String LOGIN_CODE_KEY = "login:code:";
          public static final Long LOGIN_CODE_TTL = 5L;
          public static final String LOGIN_USER_KEY = "login:token:";
          public static final Long LOGIN_USER_TTL = 360000L;
      }
      
      • 发送验证码
        @Resource
         private StringRedisTemplate stringRedisTemplate;
         @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
                stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY + phone, code, LOGIN_CODE_TTL, TimeUnit.MINUTES);
                //5.发送验证码
                log.debug("发送短信验证码成功,验证码: " + code);
               
                //返回ok
                return Result.ok();
            }
        
        • 登录
           @Override
              public Result login(LoginFormDTO loginForm, HttpSession session) { // 1.校验手机号
                  String phone = loginForm.getPhone();
                  if (!RegexUtils.isPhoneInvalid(phone)) { return Result.fail("手机格式错误");
                  }
               
                  //2.从 redis 获取,检验验证码
                  String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);
                  String code = loginForm.getCode();
                  if (cacheCode == null || !cacheCode.toString().equals(code)) { //3.不一致,报错
                      return Result.fail("验证码错误");
                  }
                  //4.一致,根据手机号查询用户
                  User user = query().eq("phone", phone).one();
                  //5.判断用户是否存在
                  if (user == null) { //6.不存在,创建用户并保存
                      user = createUserWithPhone(phone);
                  }
                 
                  // 7.保存用户信息到 redis
                  // 7.1 随机生成token,作为登录令牌
                  String token = UUID.randomUUID().toString(true);
                  // 7.2 将User对象装维HashMap存储
                  UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
                  Map userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),
                          CopyOptions.create()
                                  .setIgnoreNullValue(true)
                                  .setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString())); //idInterger类型转为String才能存
                  // 7.3 存储
                  String tokenKey = LOGIN_USER_KEY + token;
                  stringRedisTemplate.opsForHash().putAll(tokenKey, userMap);
                  stringRedisTemplate.expire(tokenKey, LOGIN_USER_TTL, TimeUnit.MINUTES);
                  // 8. 返回token
                  return Result.ok(token);
              }
          

          三、阿里云Sms服务发送短信

          参考文章:阿里云——Java实现手机短信验证码功能