《课次29:拦截器实现Token校验 + ThreadLocal》学习笔记
一、教学目标
- 创建拦截器,对除登录、注册外的接口进行 Token 校验。
- 使用
ThreadLocal存储当前登录用户的 ID,便于在后续业务处理中获取用户信息。
二、核心知识点
| 知识点 | 说明 |
|---|---|
| HandlerInterceptor | Spring MVC 提供的拦截器接口,可在请求处理前后执行自定义逻辑 |
| ThreadLocal | 线程局部变量,每个线程独立存储数据,互不干扰,适合存放当前请求的用户信息 |
| Token 校验 | 从请求头获取 Token,解析验证其有效性,无效时返回 401 状态码 |
三、操作步骤
本课次是在课次28的工程基础上继续操作。
1. 创建 UserContext 工具类
- 在
util文件夹下新建 Java 类UserContext - 代码如下:
package com.weitoutiao.util;
public class UserContext {
private static final ThreadLocal<Integer> currentUserId = new ThreadLocal<>();
public static void setCurrentUserId(Integer userId) {
currentUserId.set(userId);
}
public static Integer getCurrentUserId() {
return currentUserId.get();
}
public static void clear() {
currentUserId.remove();
}
}
说明:
ThreadLocal为每个线程独立存储数据,不同线程之间的数据互不干扰。
2. 创建 JwtInterceptor 拦截器
- 在
com.weitoutiao下新建interceptor.JwtInterceptor类 - 代码如下:
package com.weitoutiao.interceptor;
import com.weitoutiao.util.JwtUtil;
import com.weitoutiao.util.UserContext;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
@Component
public class JwtInterceptor implements HandlerInterceptor {
@Autowired
private JwtUtil jwtUtil;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 1. 从请求头中获取 Authorization
String token = request.getHeader("Authorization");
// 2. 校验 Token 是否存在且以 "Bearer " 开头
if (token != null && token.startsWith("Bearer ")) {
token = token.substring(7); // 去掉 "Bearer " 前缀
// 3. 验证 Token 是否有效
if (jwtUtil.validateToken(token)) {
Integer userId = jwtUtil.getUserIdFromToken(token);
// 4. 将用户 ID 存入 ThreadLocal
UserContext.setCurrentUserId(userId);
return true; // 放行
}
}
// 5. Token 无效或缺失,返回 401 未授权
response.setStatus(401);
response.setContentType("application/json;charset=utf-8");
response.getWriter().write("{\"code\":401,\"message\":\"未登录或token失效\"}");
return false; // 拦截
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 请求完成后,清除 ThreadLocal 中的数据,防止内存泄漏
UserContext.clear();
}
}
说明:
preHandle在请求处理之前执行,负责 Token 校验。afterCompletion在视图渲染之后(即整个请求处理完毕)执行,用于清理 ThreadLocal 资源。
3. 配置拦截器
- 在
config文件夹下新建 Java 类InterceptorConfig - 代码如下:
package com.weitoutiao.config;
import com.weitoutiao.interceptor.JwtInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Autowired
private JwtInterceptor jwtInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jwtInterceptor)
.addPathPatterns("/**") // 拦截所有请求
.excludePathPatterns("/user/login", "/user/register"); // 排除登录和注册接口
}
}
说明:
addPathPatterns("/**"):拦截所有路径的请求。excludePathPatterns(...):排除登录和注册接口,这些接口不需要 Token 验证。
4. 本课次的工程目录结构
com.weitoutiao
├── common
│ ├── GlobalExceptionHandler
│ └── Result
├── config
│ ├── InterceptorConfig ← 新增
│ ├── MyBatisPlusConfig
│ └── MyMetaObjectHandler
├── controller
│ ├── HelloController
│ └── UserController
├── dto
│ └── UserLoginDTO
├── entity
│ ├── News
│ └── User
├── interceptor ← 新增包
│ └── JwtInterceptor ← 新增
├── mapper
│ ├── NewsMapper
│ └── UserMapper
├── service
│ ├── UserService
│ └── impl
│ ├── NewsServiceImpl
│ └── UserServiceImpl
├── util
│ ├── JwtUtil
│ └── UserContext ← 新增
└── WeiTouTiaoSpringBootApplication
四、笔记总结
| 步骤 | 核心内容 |
|---|---|
| UserContext | 使用 ThreadLocal 存储当前用户 ID,提供 set、get、clear 方法 |
| JwtInterceptor | 实现 HandlerInterceptor,在 preHandle 中从请求头获取并校验 Token |
| Token 提取 | 从 Authorization 头中获取,去掉 Bearer 前缀后再验证 |
| 用户信息存储 | 验证通过后,将用户 ID 存入 ThreadLocal,供后续业务使用 |
| 资源清理 | 在 afterCompletion 中调用 UserContext.clear() 移除线程局部变量 |
| 拦截器配置 | 通过 InterceptorConfig 注册拦截器,配置拦截路径和排除路径 |
| 本课次完成了 Token 校验拦截器的实现,自此“微头条”项目的登录认证体系已完整闭环: |
- 课次28:用户登录 → 生成 JWT Token
- 课次29:请求受保护接口 → 拦截器校验 Token → 通过则放行并将用户信息存入 ThreadLocal
浙公网安备 33010602011771号