在现代 Web 开发中,认证授权、请求处理是核心环节。JWT、过滤器(Filter)、拦截器(Interceptor)作为后端开发的常用技术,常常被组合使用来解决身份验证、权限控制、请求预处理等问题。但很多开发者对它们的作用、区别和配合方式一知半解。今天我们就来系统梳理这三个技术,搞清楚它们的底层逻辑和实践场景。
一、JWT:无状态的身份令牌
首先从 JWT 说起。JWT 全称是 JSON Web Token,它是一种紧凑、自包含的令牌格式,用于在网络各方之间安全传递信息。简单来说,JWT 就是一个 “数字身份证”,能让服务器快速验证请求者的身份。
1. JWT 的结构:3 部分组成的字符串
JWT 本质是一个由.分隔的字符串,分为三部分:
- 头部(Header) :指定令牌类型(JWT)和签名算法(如 HS256、RS256),经 Base64 编码后作为第一部分。
- 载荷(Payload) :存储需要传递的核心信息(如用户 ID、角色、过期时间等),同样经 Base64 编码(注意:Base64 是编码不是加密,不要存敏感信息)。
- 签名(Signature) :用头部指定的算法,结合密钥对 “头部 + 载荷” 进行加密生成的字符串,用于验证令牌是否被篡改。
2. JWT 的工作流程
JWT 的核心价值是 “无状态认证”,流程如下:
- 登录生成令牌:用户提交账号密码,服务器验证通过后,生成包含用户信息(如 ID、角色)和过期时间的 JWT,返回给客户端。
- 后续请求携带令牌:客户端(如浏览器、APP)在后续请求中,通过
Authorization请求头(格式:Bearer <token>)携带 JWT。 - 服务器验证令牌:服务器接收请求后,解析 JWT 的签名,验证令牌是否有效(未过期、未被篡改),验证通过则认可用户身份。
二、过滤器(Filter):请求的 “第一道关卡”
过滤器是 Servlet 规范定义的组件,作用于请求进入 Servlet 之前、响应返回客户端之后,用于对请求 / 响应进行通用处理。
优势:服务器无需存储令牌(无状态),适合分布式系统;自包含信息减少数据库查询。
局限:令牌一旦签发无法主动撤销(除非结合黑名单);载荷仅 Base64 编码,不能存敏感信息。
1. 核心特性
-
基于函数回调:依赖 Servlet 容器,与 Spring 框架无关。
-
作用范围广:可处理所有请求(包括静态资源、非 Spring MVC 请求)。
-
生命周期:随 Web 应用启动初始化,随应用停止销毁;每次请求触发
doFilter方法。
常见用途:编码转换(如设置UTF-8)、日志记录、Token 验证(如 JWT 校验)、跨域处理(CORS)等。
2. 实现方式
自定义过滤器需实现javax.servlet.Filter接口,重写三个方法:
-
init():初始化方法,Web 应用启动时执行。 -
doFilter():核心方法,处理请求 / 响应,通过chain.doFilter(request, response)放行请求。 -
destroy():销毁方法,Web 应用停止时执行。 示例:用过滤器验证 JWT
java
public class JwtFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
// 1. 获取请求头中的Token
String token = httpRequest.getHeader("Authorization");
if (token == null || !token.startsWith("Bearer ")) {
httpResponse.setStatus(401);
httpResponse.getWriter().write("未携带有效Token");
return;
}
token = token.substring(7); // 去除"Bearer "前缀
// 2. 验证JWT
try {
JwtUtils.verify(token); // 自定义JWT验证工具类
chain.doFilter(request, response); // 验证通过,放行
} catch (Exception e) {
httpResponse.setStatus(401);
httpResponse.getWriter().write("Token无效或已过期");
}
}
}
java
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<JwtFilter> jwtFilter() {
FilterRegistrationBean<JwtFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new JwtFilter());
registrationBean.addUrlPatterns("/api/*"); // 拦截/api/*路径的请求
registrationBean.setOrder(1); // 过滤器执行顺序(数字越小越先执行)
return registrationBean;
}
}
三、拦截器(Interceptor):Controller 的 “贴身保镖”
拦截器是 Spring MVC 提供的组件,作用于 Controller 方法调用前后,依赖 Spring 容器,更贴近业务逻辑处理。
1. 核心特性
-
基于 AOP 思想:属于 Spring 框架,可访问 Spring 上下文(如
ApplicationContext、Bean)。 -
作用范围窄:仅处理 Spring MVC 的请求(即通过
DispatcherServlet的请求)。 -
细粒度控制:可获取
HandlerMethod(Controller 方法对象),能针对具体方法做处理。
常见用途:权限校验(如验证用户是否有某接口的访问权限)、性能监控(记录接口执行时间)、日志埋点等。
2. 实现方式
自定义拦截器需实现org.springframework.web.servlet.HandlerInterceptor接口,重写三个方法:
-
preHandle():Controller 方法执行前调用,返回true放行,false拦截。 -
postHandle():Controller 方法执行后、视图渲染前调用。 -
afterCompletion():视图渲染后、响应返回前调用(常用于资源清理)。
示例:用拦截器验证用户权限
java
public class PermissionInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 1. 判断是否为Controller方法(排除静态资源等)
if (!(handler instanceof HandlerMethod)) {
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) handler;
// 2. 获取接口所需权限(假设通过@RequiredPermission注解定义)
RequiredPermission annotation = handlerMethod.getMethodAnnotation(RequiredPermission.class);
if (annotation == null) {
return true; // 无权限注解,直接放行
}
String requiredPerm = annotation.value();
// 3. 从请求中获取当前用户权限(假设JWT验证后已将用户信息存入request)
User user = (User) request.getAttribute("currentUser");
if (user == null || !user.getPermissions().contains(requiredPerm)) {
response.setStatus(403);
response.getWriter().write("无访问权限");
return false; // 拦截请求
}
return true; // 权限通过,放行
}
}
注册拦截器(需配置在 Spring MVC 中):
java
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new PermissionInterceptor())
.addPathPatterns("/api/**") // 拦截/api/*路径
.excludePathPatterns("/api/login"); // 排除登录接口
}
}
过滤器与拦截器的核心区别
很多人会混淆过滤器和拦截器,核心区别如下:
| 维度 | 过滤器(Filter) | 拦截器(Interceptor) |
|---|---|---|
| 依赖 | Servlet 容器(与 Spring 无关) | Spring MVC 框架 |
| 作用时机 | 请求进入 Servlet 之前 | Controller 方法执行前后 |
| 处理范围 | 所有请求(包括非 Spring 请求) | 仅 Spring MVC 请求 |
| 可访问对象 | 仅ServletRequest/ServletResponse | 可访问HandlerMethod、Spring 上下文 |
| 执行顺序 | 随注册顺序执行(Order值越小越先) | 随注册顺序执行(Order值越小越先) |
四、三者结合:构建完整的认证授权流程
在实际开发中,JWT、过滤器、拦截器常常配合使用,形成 “认证 - 授权” 的完整链路:
- 过滤器负责 JWT 验证:请求进入时,过滤器先校验 JWT 是否有效。无效则直接返回 401;有效则解析用户信息(如用户 ID、角色),存入
request属性中。 - 拦截器负责权限校验:过滤器放行后,拦截器在
preHandle中获取request中的用户信息,结合HandlerMethod判断用户是否有当前接口的访问权限。无权限则返回 403;有权限则放行至 Controller。 - Controller 处理业务:经过认证和授权后,Controller 专注于业务逻辑处理。