使用 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 线程复用时可能会导致用户数据混乱。

Categories:

Tags:

暂时没有回复

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注