一、登录认证拦截器实现
1. 核心目标
- 所有接口统一校验
Authorization请求头中的 JWT 令牌 - 放行
/user/login和/user/register接口,无需令牌即可访问
2. 拦截器代码(LoginInterceptor.java)
java
运行
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 1. 获取请求头中的令牌
String token = request.getHeader("Authorization");
try {
// 2. 解析并验证令牌
Map<String, Object> claims = JwtUtil.parseToken(token);
// 3. 将用户信息存入 ThreadLocal,供后续业务使用
ThreadLocalUtil.set(claims);
// 4. 校验通过,放行请求
return true;
} catch (Exception e) {
// 令牌无效/过期,设置响应状态码并拒绝请求
response.setStatus(401);
return false;
}
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 请求结束后,清除 ThreadLocal 数据,防止内存泄漏
ThreadLocalUtil.remove();
}
}
3. 注册拦截器(WebConfig.java)
java
运行
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
// 拦截所有请求
.addPathPatterns("/**")
// 放行登录、注册接口
.excludePathPatterns("/user/login", "/user/register");
}
}
二、ThreadLocal 工具类实现
1. 核心作用
- 提供线程局部变量,同一请求的所有层级(Controller/Service/Mapper)都能安全访问用户信息
- 线程安全,用完必须调用
remove()释放数据,避免内存泄漏
2. 工具类代码(ThreadLocalUtil.java)
java
运行
public class ThreadLocalUtil {
// 提供 ThreadLocal 对象
private static final ThreadLocal THREAD_LOCAL = new ThreadLocal();
// 根据键获取值
public static <T> T get() {
return (T) THREAD_LOCAL.get();
}
// 存储键值对
public static void set(Object value) {
THREAD_LOCAL.set(value);
}
// 清除 ThreadLocal,防止内存泄漏
public static void remove() {
THREAD_LOCAL.remove();
}
}
三、业务接口改造(获取用户信息)
1. Controller 层接口
java
运行
@GetMapping("/userInfo")
public Result<User> userInfo() {
// 从 ThreadLocal 中获取解析后的用户信息
Map<String, Object> claims = ThreadLocalUtil.get();
String username = (String) claims.get("username");
// 调用 Service 查询用户信息
User user = userService.findByUsername(username);
return Result.success(user);
}
2. 复用 Service & Mapper 层
UserService.findByUsername(username):直接复用注册 / 登录时的查询方法UserMapper.select * from user where username = ?:复用已有的查询 SQL
四、关键注意事项
- 拦截器放行配置 :
/user/login和/user/register必须放行,否则用户无法登录获取令牌 - ThreadLocal 内存泄漏 :请求结束后必须调用
remove(),否则 Tomcat 线程复用会导致数据混乱 - 令牌校验失败处理 :拦截器中校验失败直接返回
401状态码,前端可据此跳转登录页 - 请求头名称统一 :前后端约定令牌都放在
Authorization请求头中,避免出现字段名不一致的问题
五、完整流程总结
- 用户登录成功,后端生成 JWT 令牌返回给前端
- 前端后续请求携带令牌,请求头
Authorization: 令牌 LoginInterceptor拦截请求,解析验证令牌,将用户信息存入ThreadLocal- 业务接口从
ThreadLocal中获取用户信息,完成后续操作 - 请求结束,
afterCompletion中清除ThreadLocal数据