使用 UUID 作为 Token,在 Redis 里存储用户信息,然后每次请求时通过 UUID 获取用户上下文。
UUID 认证流程
1. 用户登录
- 用户输入账号密码,服务器验证通过后,生成 UUID 作为 Token。
- 把 UUID 和用户信息存入 Redis,并设置有效期(如 30 分钟)。
- 把 UUID 返回给前端,前端存入 LocalStorage / Cookie。
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest request) {
UserInfo user = userService.authenticate(request.getUsername(), request.getPassword());
if (user == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("用户名或密码错误");
}
// 生成 UUID 作为 Token
String token = UUID.randomUUID().toString();
// 存入 Redis,设置过期时间
redisTemplate.opsForValue().set("TOKEN_" + token, user, 30, TimeUnit.MINUTES);
// 返回 Token 给前端
return ResponseEntity.ok(Map.of("token", token));
}
2. 解析用户上下文
每次请求时,前端在 Authorization 头部传递 UUID Token,后端通过 Redis 查询用户信息,并存入 ThreadLocal。
@Component
public class TokenInterceptor implements HandlerInterceptor {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("Authorization");
if (StringUtils.hasText(token)) {
UserInfo user = (UserInfo) redisTemplate.opsForValue().get("TOKEN_" + token);
if (user != null) {
UserContext.setUser(user); // 存入 ThreadLocal
return true; // 放行
}
}
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("未登录或Token无效");
return false;
}
}
注册拦截器
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private TokenInterceptor tokenInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(tokenInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/auth/login", "/auth/register"); // 放行登录、注册
}
}
3. 获取当前用户
由于用户信息已存入 ThreadLocal,在 Controller 或 Service 里可以随时获取:
public class UserContext {
private static final ThreadLocal<UserInfo> userHolder = new ThreadLocal<>();
public static void setUser(UserInfo user) {
userHolder.set(user);
}
public static UserInfo getUser() {
return userHolder.get();
}
public static void clear() {
userHolder.remove();
}
}
使用示例:
@GetMapping("/user/info")
public ResponseEntity<?> getUserInfo() {
UserInfo user = UserContext.getUser();
return ResponseEntity.ok(user);
}
4. 退出登录
- 前端请求 /logout,服务器删除 Redis 里的 UUID Token,并清理 ThreadLocal。
@PostMapping("/logout")
public ResponseEntity<?> logout(HttpServletRequest request) {
String token = request.getHeader("Authorization");
if (StringUtils.hasText(token)) {
redisTemplate.delete("TOKEN_" + token);
}
return ResponseEntity.ok("退出成功");
}
总结
| 步骤 | 处理方式 |
|---|---|
| 登录 | 生成 UUID,存入 Redis,返回给前端 |
| 请求时校验 | 解析 Authorization 头部 UUID,从 Redis 获取用户信息,存入 ThreadLocal |
| 获取当前用户 | 通过 UserContext.getUser() 获取 ThreadLocal 里的用户信息 |
| 退出登录 | 删除 Redis 里的 UUID,前端删除 Token |
如果 UserContext 是 ThreadLocal,一定要在请求结束时 清理数据,否则Tomcat 线程复用时可能会导致用户数据混乱。
暂时没有回复