Redis存储Token实现短信登录方案
SryYa Three

登录流程

image

实现登录功能

  • Controller
UserController
1
2
3
4
5
6
7
8
9
10
@PostMapping("/code")  
public Result sendCode(@RequestParam("phone") String phone, HttpSession session) {
userService.sendCode(phone, session);
return Result.ok("发送验证码");
}

@PostMapping("/login")
public Result login(@RequestBody LoginFormDTO loginForm, HttpSession session) {
return userService.login(loginForm, session);
}
  • ServiceImpl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public Result login(LoginFormDTO loginForm, HttpSession session) {  
//获取前端输入的手机号
String phone = loginForm.getPhone();
//获取前端输入的验证码
String loginCode = loginForm.getCode();
if (RegexUtils.isPhoneInvalid(phone)) {
return Result.fail("手机号错误");
}
//从redis获取后端生成的验证码
String redisCode = stringRedisTemplate.opsForValue().get(SystemContext.LOGIN_CODE + phone);
//前后端验证码不一致时
if(!loginCode.equals(redisCode))
{
return Result.fail("验证码错误");
}
//根据手机号从数据库查询用户
User user = query().eq("phone", phone).one();
//用户不存在,创建用户并保存到数据库
if (user == null) {
user = creatUserWithPhone(phone);
}
//生成token
String token = UUID.randomUUID().toString();
//存储token和user信息到redis
stringRedisTemplate.opsForValue().set(SystemContext.LOGIN_TOKEN + token, JSONUtil.toJsonStr(user),2,TimeUnit.HOURS);
return Result.ok(token);
}

拦截器拦截

用于延期Token的刷新拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class RefreshTokenInterceptor implements HandlerInterceptor {  
@Resource
private StringRedisTemplate stringRedisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("拦截器拦截");
//从请求中获取token
String token = request.getHeader("authorization");
if (StringUtils.isEmpty(token) || "undefined".equals(token)) {
//不存在token
//放行到登录校验拦截器(以下都是)
return true;
}
log.info("获得的token:{}",token);
//获取user的json字符串
String userStr = stringRedisTemplate.opsForValue().get(SystemContext.LOGIN_TOKEN + token);
//转换成user对象
User user = JSONUtil.toBean(userStr,User.class);
log.info("拦截到user:{}",user);
if (user == null) {
return true;
}
//把user转换成userDTO并保存到ThreadLocal
UserDTO userDTO = getUserDTOAndSaveToThreadLocal(user);
log.info("保存用户:{}到ThreadLocal",userDTO);
//续期Token
stringRedisTemplate.expire(SystemContext.LOGIN_TOKEN + token,2, TimeUnit.HOURS);
return true;
}

@NotNull
private static UserDTO getUserDTOAndSaveToThreadLocal(User user) {
UserDTO userDTO = new UserDTO();
userDTO.setId(user.getId());
userDTO.setNickName(user.getNickName());
userDTO.setIcon(user.getIcon());
UserHolder.saveUser(userDTO);
return userDTO;
}
}

校验登录拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class LoginInterceptor implements HandlerInterceptor {  
@Resource
private StringRedisTemplate stringRedisTemplate;

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
log.info("ThreadLocal清除用户");
UserHolder.removeUser();
}


@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//从ThreadLocal里面查询用户
if (UserHolder.getUser() == null) {
response.setStatus(401);
return false;
}
//
return true;
}

注册拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class MvcConfig implements WebMvcConfigurer {  
@Resource
private LoginInterceptor loginInterceptor;
@Resource
private RefreshTokenInterceptor refreshTokenInterceptor;

@Override
public void addInterceptors(InterceptorRegistry registry) {
log.info("加载拦截器");
registry.addInterceptor(loginInterceptor)
.excludePathPatterns(
"/user/code", // 发送验证码接口
"/user/login", // 登录接口
"/blog/hot", // 首页热门博客
"/shop/**", // 店铺信息
"/shop-type/**", // 店铺分类
"/upload/**",
"/voucher/**"
)
.order(1);
registry.addInterceptor(refreshTokenInterceptor).addPathPatterns("/**").order(0);
}
}
由 Hexo 驱动 & 主题 Keep
总字数 28.1k 访客数 访问量