拦截器与过滤器链机制
文章目录
- 拦截器与过滤器链机制
 - 🎯 一、Filter 与 Interceptor 架构对比
 - 💡 定位与作用域差异
 - 🏗️ 执行流程架构
 
- 🔄 二、DispatcherServlet 请求链路拦截点
 - 🔧 核心拦截点源码分析
 - 📊 HandlerExecutionChain 拦截器管理
 
- ⏱️ 三、拦截器方法调用顺序深度解析
 - 🔄 多拦截器执行时序
 - ⚡ 拦截器配置与顺序控制
 - 🔍 拦截器执行监控
 
- 🛡️ 四、实战:企业级接口鉴权拦截器
 - 💡 JWT 认证拦截器实现
 - 🔐 权限控制拦截器
 - 📊 完整的拦截器配置
 
- 💡 五、性能优化与监控体系
 - ⚡ 拦截器性能优化
 - 📈 拦截器监控体系
 - 🔧 过滤器与拦截器协作配置
 
拦截器与过滤器链机制
🎯 一、Filter 与 Interceptor 架构对比
💡 定位与作用域差异
架构层次对比图:
核心差异对比表:
| 特性 | Servlet Filter | Spring Interceptor | 差异分析 / 设计价值 | 
|---|---|---|---|
| 规范层级 | 基于 Servlet 规范(J2EE 标准) | 属于 Spring MVC 框架扩展层 | Filter 是底层容器能力,Interceptor 是框架级增强 | 
| 依赖容器 | 不依赖 Spring 容器,可独立运行 | 完全依赖 Spring 上下文 | Interceptor 能与 IoC、AOP、事务管理无缝协作 | 
| 作用范围 | 处理所有 Web 请求(包括静态资源) | 仅拦截 Spring MVC 控制器请求 | Filter 更通用,Interceptor 更聚焦业务逻辑 | 
| 获取 Bean | 需通过 WebApplicationContextUtils 手动获取 | 可直接 @Autowired 注入 | Interceptor 更符合 Spring 编程模型 | 
| 执行顺序 | 在 Interceptor 之前执行 | 在 Filter 之后执行 | Filter 适合安全、日志类预处理;Interceptor 适合控制层逻辑 | 
| 异常处理 | 仅能捕获 Servlet 层异常 | 可参与 Spring 异常体系(@ControllerAdvice) | Interceptor 能实现更细粒度的异常与返回统一化 | 
| 配置方式 | web.xml / @WebFilter 注解 | 注册到 WebMvcConfigurer 中 | Interceptor 配置更灵活、可按路径精确控制 | 
| 典型用途 | 编码过滤、XSS 防护、全局安全过滤 | 登录校验、权限验证、操作日志、性能监控 | 可联合使用,形成双层安全与逻辑拦截链 | 
🏗️ 执行流程架构
完整请求处理流水线:
🔄 二、DispatcherServlet 请求链路拦截点
🔧 核心拦截点源码分析
DispatcherServlet doDispatch 方法拦截点:
public class DispatcherServlet extends FrameworkServlet {protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HandlerExecutionChain mappedHandler = null;ModelAndView mv = null;Exception dispatchException = null;try {// 1. 文件上传检查点processedRequest = checkMultipart(request);// 2. 获取处理器执行链(包含拦截器)mappedHandler = getHandler(processedRequest);if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}// 3. 获取处理器适配器HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// 📍 拦截点1: 执行所有拦截器的preHandle方法if (!mappedHandler.applyPreHandle(processedRequest, response)) {return; // 如果任一拦截器返回false,直接返回}// 4. 实际执行处理器(Controller方法)mv = ha.handle(processedRequest, response, mappedHandler.getHandler());// 5. 应用默认视图名applyDefaultViewName(processedRequest, mv);// 📍 拦截点2: 执行所有拦截器的postHandle方法(逆序)mappedHandler.applyPostHandle(processedRequest, response, mv);} catch (Exception ex) {dispatchException = ex;} finally {// 📍 拦截点3: 执行所有拦截器的afterCompletion方法if (mappedHandler != null) {mappedHandler.triggerAfterCompletion(processedRequest, response, dispatchException);}}}
}
 
📊 HandlerExecutionChain 拦截器管理
拦截器链执行核心逻辑:
public class HandlerExecutionChain {private final Object handler;private final HandlerInterceptor[] interceptors;private int interceptorIndex = -1;/*** 执行preHandle拦截*/boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {for (int i = 0; i < this.interceptors.length; i++) {HandlerInterceptor interceptor = this.interceptors[i];if (!interceptor.preHandle(request, response, this.handler)) {// 拦截器返回false,触发已完成拦截器的afterCompletiontriggerAfterCompletion(request, response, null);return false;}this.interceptorIndex = i; // 记录最后一个成功的拦截器索引}return true;}/*** 执行postHandle拦截(逆序)*/void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView modelAndView) throws Exception {for (int i = this.interceptors.length - 1; i >= 0; i--) {HandlerInterceptor interceptor = this.interceptors[i];interceptor.postHandle(request, response, this.handler, modelAndView);}}/*** 执行afterCompletion拦截*/void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex) throws Exception {for (int i = this.interceptorIndex; i >= 0; i--) {HandlerInterceptor interceptor = this.interceptors[i];try {interceptor.afterCompletion(request, response, this.handler, ex);} catch (Throwable ex2) {logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);}}}
}
 
⏱️ 三、拦截器方法调用顺序深度解析
🔄 多拦截器执行时序
完整拦截器调用时序图:
⚡ 拦截器配置与顺序控制
精细化拦截器配置:
@Configuration
@Slf4j
public class InterceptorConfiguration implements WebMvcConfigurer {@Autowiredprivate LoggingInterceptor loggingInterceptor;@Autowiredprivate AuthInterceptor authInterceptor;@Autowiredprivate PermissionInterceptor permissionInterceptor;@Autowiredprivate RateLimitInterceptor rateLimitInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 1. 日志拦截器(最外层,最先执行)registry.addInterceptor(loggingInterceptor).order(Ordered.HIGHEST_PRECEDENCE) // 最高优先级.addPathPatterns("/api/**").excludePathPatterns("/api/health", "/api/metrics").addPathPatterns("/admin/**");// 2. 限流拦截器registry.addInterceptor(rateLimitInterceptor).order(Ordered.HIGHEST_PRECEDENCE + 10).addPathPatterns("/api/**").excludePathPatterns("/api/static/**");// 3. 认证拦截器registry.addInterceptor(authInterceptor).order(Ordered.HIGHEST_PRECEDENCE + 20).addPathPatterns("/api/**").excludePathPatterns("/api/public/**", "/api/auth/login");// 4. 权限拦截器(最内层,最后执行)registry.addInterceptor(permissionInterceptor).order(Ordered.LOWEST_PRECEDENCE) // 最低优先级.addPathPatterns("/api/**", "/admin/**").excludePathPatterns("/api/public/**");}
}
 
🔍 拦截器执行监控
拦截器执行跟踪工具:
@Component
@Slf4j
public class InterceptorExecutionTracer {private static final ThreadLocal<InterceptorTrace> traceHolder = new ThreadLocal<>();/*** 开始跟踪*/public void startTrace(HttpServletRequest request) {InterceptorTrace trace = new InterceptorTrace();trace.setStartTime(System.currentTimeMillis());trace.setRequestPath(request.getRequestURI());traceHolder.set(trace);}/*** 记录拦截器执行*/public void recordInterceptorExecution(String interceptorName, String phase, long duration) {InterceptorTrace trace = traceHolder.get();if (trace != null) {trace.addExecutionRecord(interceptorName, phase, duration);}}/*** 完成跟踪并输出报告*/public void completeTrace() {InterceptorTrace trace = traceHolder.get();if (trace != null) {log.info("拦截器执行跟踪报告: {}", trace.generateReport());traceHolder.remove();}}@Datapublic static class InterceptorTrace {private long startTime;private String requestPath;private List<ExecutionRecord> records = new ArrayList<>();public void addExecutionRecord(String interceptorName, String phase, long duration) {records.add(new ExecutionRecord(interceptorName, phase, duration));}public String generateReport() {StringBuilder report = new StringBuilder();report.append("路径: ").append(requestPath).append("\n");report.append("总耗时: ").append(System.currentTimeMillis() - startTime).append("ms\n");report.append("拦截器执行详情:\n");for (ExecutionRecord record : records) {report.append(String.format("  %s - %s: %dms\n", record.interceptorName, record.phase, record.duration));}return report.toString();}}@Data@AllArgsConstructorpublic static class ExecutionRecord {private String interceptorName;private String phase;private long duration;}
}
 
🛡️ 四、实战:企业级接口鉴权拦截器
💡 JWT 认证拦截器实现
完整的认证拦截器实现:
@Component
@Slf4j
public class JwtAuthenticationInterceptor implements HandlerInterceptor {@Autowiredprivate JwtTokenProvider tokenProvider;@Autowiredprivate UserService userService;private static final String AUTH_HEADER = "Authorization";private static final String BEARER_PREFIX = "Bearer ";private static final String USER_ATTR = "currentUser";@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 1. 检查是否是需要跳过的路径if (shouldSkipAuth(request)) {return true;}// 2. 提取并验证TokenString token = extractToken(request);if (token == null) {sendUnauthorizedResponse(response, "缺少认证令牌");return false;}// 3. 验证Token有效性if (!tokenProvider.validateToken(token)) {sendUnauthorizedResponse(response, "令牌无效或已过期");return false;}// 4. 解析用户信息并设置到请求上下文中try {Authentication authentication = createAuthentication(token);SecurityContextHolder.getContext().setAuthentication(authentication);// 5. 将用户信息设置到请求属性中UserDetails userDetails = (UserDetails) authentication.getPrincipal();request.setAttribute(USER_ATTR, userDetails);log.debug("用户认证成功: {} - {}", userDetails.getUsername(), request.getRequestURI());return true;} catch (Exception ex) {log.error("令牌解析失败", ex);sendUnauthorizedResponse(response, "令牌解析失败");return false;}}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {// 清理安全上下文,防止内存泄漏SecurityContextHolder.clearContext();}/*** 提取JWT令牌*/private String extractToken(HttpServletRequest request) {String bearerToken = request.getHeader(AUTH_HEADER);if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(BEARER_PREFIX)) {return bearerToken.substring(BEARER_PREFIX.length());}return null;}/*** 检查是否需要跳过认证*/private boolean shouldSkipAuth(HttpServletRequest request) {String path = request.getRequestURI();// 1. 检查白名单路径List<String> whiteList = Arrays.asList("/api/public/", "/api/auth/login", "/api/auth/register", "/api/docs", "/swagger-ui", "/v3/api-docs");if (whiteList.stream().anyMatch(path::startsWith)) {return true;}// 2. 检查注解配置if (handlerRequiresNoAuth(handler)) {return true;}return false;}/*** 检查处理器上的注解*/private boolean handlerRequiresNoAuth(Object handler) {if (handler instanceof HandlerMethod) {HandlerMethod handlerMethod = (HandlerMethod) handler;// 检查方法上的@PublicApi注解if (handlerMethod.getMethodAnnotation(PublicApi.class) != null) {return true;}// 检查类上的@PublicApi注解if (handlerMethod.getBeanType().isAnnotationPresent(PublicApi.class)) {return true;}}return false;}/*** 创建认证信息*/private Authentication createAuthentication(String token) {String username = tokenProvider.getUsernameFromToken(token);UserDetails userDetails = userService.loadUserByUsername(username);return new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());}/*** 发送未认证响应*/private void sendUnauthorizedResponse(HttpServletResponse response, String message) throws IOException {response.setStatus(HttpStatus.UNAUTHORIZED.value());response.setContentType(MediaType.APPLICATION_JSON_VALUE);Map<String, Object> errorResponse = new HashMap<>();errorResponse.put("success", false);errorResponse.put("code", "UNAUTHORIZED");errorResponse.put("message", message);errorResponse.put("timestamp", System.currentTimeMillis());ObjectMapper mapper = new ObjectMapper();response.getWriter().write(mapper.writeValueAsString(errorResponse));}
}
 
🔐 权限控制拦截器
基于注解的权限控制:
@Component  
@Slf4j
public class PermissionInterceptor implements HandlerInterceptor {@Autowiredprivate PermissionService permissionService;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {if (!(handler instanceof HandlerMethod)) {return true;}HandlerMethod handlerMethod = (HandlerMethod) handler;// 1. 检查权限注解RequirePermission requirePermission = handlerMethod.getMethodAnnotation(RequirePermission.class);if (requirePermission == null) {return true; // 没有权限要求,直接放行}// 2. 获取当前用户Authentication authentication = SecurityContextHolder.getContext().getAuthentication();if (authentication == null || !authentication.isAuthenticated()) {sendForbiddenResponse(response, "用户未认证");return false;}// 3. 验证权限String[] requiredPermissions = requirePermission.value();Logical logical = requirePermission.logical();boolean hasPermission = permissionService.hasPermissions(authentication, requiredPermissions, logical);if (!hasPermission) {log.warn("权限不足: 用户={}, 路径={}, 所需权限={}", authentication.getName(), request.getRequestURI(), Arrays.toString(requiredPermissions));sendForbiddenResponse(response, "权限不足");return false;}log.debug("权限验证通过: 用户={}, 路径={}", authentication.getName(), request.getRequestURI());return true;}private void sendForbiddenResponse(HttpServletResponse response, String message) throws IOException {response.setStatus(HttpStatus.FORBIDDEN.value());response.setContentType(MediaType.APPLICATION_JSON_VALUE);Map<String, Object> errorResponse = new HashMap<>();errorResponse.put("success", false);errorResponse.put("code", "FORBIDDEN");errorResponse.put("message", message);errorResponse.put("timestamp", System.currentTimeMillis());ObjectMapper mapper = new ObjectMapper();response.getWriter().write(mapper.writeValueAsString(errorResponse));}
}/*** 权限验证注解*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequirePermission {String[] value() default {};Logical logical() default Logical.AND;
}/*** 逻辑操作枚举*/
public enum Logical {AND, OR
}/*** 公开API注解(跳过认证)*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PublicApi {
}
 
📊 完整的拦截器配置
生产级拦截器链配置:
@Configuration
@EnableWebMvc
@Slf4j
public class SecurityInterceptorConfig implements WebMvcConfigurer {@Autowiredprivate JwtAuthenticationInterceptor jwtAuthInterceptor;@Autowiredprivate PermissionInterceptor permissionInterceptor;@Autowiredprivate LoggingInterceptor loggingInterceptor;@Autowiredprivate RateLimitInterceptor rateLimitInterceptor;@Autowiredprivate CorsInterceptor corsInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {log.info("配置拦截器链...");// 1. CORS拦截器(最外层)registry.addInterceptor(corsInterceptor).order(Ordered.HIGHEST_PRECEDENCE).addPathPatterns("/**");// 2. 日志拦截器registry.addInterceptor(loggingInterceptor).order(Ordered.HIGHEST_PRECEDENCE + 10).addPathPatterns("/api/**", "/admin/**").excludePathPatterns("/health", "/metrics", "/static/**");// 3. 限流拦截器registry.addInterceptor(rateLimitInterceptor).order(Ordered.HIGHEST_PRECEDENCE + 20).addPathPatterns("/api/**").excludePathPatterns("/api/static/**");// 4. 认证拦截器registry.addInterceptor(jwtAuthInterceptor).order(Ordered.HIGHEST_PRECEDENCE + 30).addPathPatterns("/api/**", "/admin/**").excludePathPatterns("/api/public/**", "/api/auth/**", "/api/docs/**");// 5. 权限拦截器(最内层)registry.addInterceptor(permissionInterceptor).order(Ordered.LOWEST_PRECEDENCE).addPathPatterns("/api/**", "/admin/**").excludePathPatterns("/api/public/**");log.info("拦截器链配置完成");}
}
 
💡 五、性能优化与监控体系
⚡ 拦截器性能优化
高性能拦截器实现:
@Component
@Slf4j
public class PerformanceOptimizedInterceptor implements HandlerInterceptor {private final ConcurrentMap<String, Boolean> pathCache = new ConcurrentHashMap<>();private final RateLimiter rateLimiter = RateLimiter.create(1000); // 每秒1000次请求@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {long startTime = System.nanoTime();try {// 1. 限流保护if (!rateLimiter.tryAcquire()) {sendTooManyRequestsResponse(response);return false;}// 2. 路径匹配缓存优化String path = request.getRequestURI();Boolean shouldProcess = pathCache.computeIfAbsent(path, this::shouldProcessPath);if (!shouldProcess) {return true; // 跳过处理}// 3. 异步处理耗时操作if (requiresAsyncProcessing(request)) {CompletableFuture.runAsync(() -> processAsync(request));}return true;} finally {long duration = System.nanoTime() - startTime;if (duration > 100_000_000) { // 超过100ms记录警告log.warn("拦截器执行缓慢: {}ms, 路径: {}", duration / 1_000_000, request.getRequestURI());}}}private boolean shouldProcessPath(String path) {// 实现智能路径匹配逻辑return !path.startsWith("/static/") && !path.equals("/health") && !path.contains("websocket");}private boolean requiresAsyncProcessing(HttpServletRequest request) {return request.getRequestURI().contains("/analytics/") ||request.getRequestURI().contains("/monitoring/");}private void processAsync(HttpServletRequest request) {// 异步处理分析、监控等耗时操作try {// 模拟异步处理Thread.sleep(50);log.debug("异步处理完成: {}", request.getRequestURI());} catch (InterruptedException e) {Thread.currentThread().interrupt();}}private void sendTooManyRequestsResponse(HttpServletResponse response) throws IOException {response.setStatus(429);response.setContentType("application/json");response.getWriter().write("{\"error\":\"请求过于频繁\"}");}
}
 
📈 拦截器监控体系
拦截器监控与指标收集:
@Component
@Slf4j
public class InterceptorMetricsCollector {private final MeterRegistry meterRegistry;private final Map<String, Timer> timerMap = new ConcurrentHashMap<>();private final Map<String, Counter> counterMap = new ConcurrentHashMap<>();public InterceptorMetricsCollector(MeterRegistry meterRegistry) {this.meterRegistry = meterRegistry;}/*** 记录拦截器执行时间*/public void recordInterceptorExecution(String interceptorName, String phase, long duration) {String timerName = "interceptor." + interceptorName + "." + phase;Timer timer = timerMap.computeIfAbsent(timerName, name -> Timer.builder(name).description(interceptorName + " " + phase + "执行时间").register(meterRegistry));timer.record(duration, TimeUnit.MILLISECONDS);}/*** 记录拦截器执行次数*/public void recordInterceptorInvocation(String interceptorName, boolean success) {String status = success ? "success" : "failure";String counterName = "interceptor." + interceptorName + ".invocations";Counter counter = counterMap.computeIfAbsent(counterName + "." + status,name -> Counter.builder(name).description(interceptorName + "调用次数").tag("status", status).register(meterRegistry));counter.increment();}/*** 生成拦截器性能报告*/public Map<String, Object> generateInterceptorReport() {Map<String, Object> report = new HashMap<>();// 收集计时器数据Map<String, Object> timerStats = new HashMap<>();timerMap.forEach((name, timer) -> {Timer.Snapshot snapshot = timer.takeSnapshot();Map<String, Object> stats = new HashMap<>();stats.put("count", snapshot.count());stats.put("mean", snapshot.mean(TimeUnit.MILLISECONDS));stats.put("max", snapshot.max(TimeUnit.MILLISECONDS));timerStats.put(name, stats);});report.put("timers", timerStats);// 收集计数器数据Map<String, Object> counterStats = new HashMap<>();counterMap.forEach((name, counter) -> {counterStats.put(name, counter.count());});report.put("counters", counterStats);return report;}
}/*** 可监控的拦截器基类*/
public abstract class MonitorableInterceptor implements HandlerInterceptor {@Autowiredprivate InterceptorMetricsCollector metricsCollector;private final String interceptorName;protected MonitorableInterceptor() {this.interceptorName = this.getClass().getSimpleName();}@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {long startTime = System.currentTimeMillis();boolean success = false;try {success = doPreHandle(request, response, handler);return success;} finally {long duration = System.currentTimeMillis() - startTime;metricsCollector.recordInterceptorExecution(interceptorName, "preHandle", duration);metricsCollector.recordInterceptorInvocation(interceptorName, success);}}protected abstract boolean doPreHandle(HttpServletRequest request, HttpServletResponse response, Object handler);
}
 
🔧 过滤器与拦截器协作配置
完整的Web安全配置:
@Configuration
@Slf4j
public class WebSecurityConfig {/*** 配置过滤器链*/@Beanpublic FilterRegistrationBean<RequestContextFilter> requestContextFilter() {FilterRegistrationBean<RequestContextFilter> registrationBean = new FilterRegistrationBean<>();registrationBean.setFilter(new RequestContextFilter());registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);return registrationBean;}/*** 字符编码过滤器*/@Beanpublic FilterRegistrationBean<CharacterEncodingFilter> characterEncodingFilter() {FilterRegistrationBean<CharacterEncodingFilter> registrationBean = new FilterRegistrationBean<>();CharacterEncodingFilter filter = new CharacterEncodingFilter();filter.setEncoding("UTF-8");filter.setForceEncoding(true);registrationBean.setFilter(filter);registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE + 10);registrationBean.addUrlPatterns("/*");return registrationBean;}/*** 请求日志过滤器*/@Beanpublic FilterRegistrationBean<RequestLoggingFilter> requestLoggingFilter() {FilterRegistrationBean<RequestLoggingFilter> registrationBean = new FilterRegistrationBean<>();RequestLoggingFilter filter = new RequestLoggingFilter();registrationBean.setFilter(filter);registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE + 20);registrationBean.addUrlPatterns("/api/*", "/admin/*");return registrationBean;}
}/*** 自定义请求日志过滤器*/
@Slf4j
public class RequestLoggingFilter implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {long startTime = System.currentTimeMillis();HttpServletRequest httpRequest = (HttpServletRequest) request;try {// 前置处理:记录请求开始logRequestStart(httpRequest);chain.doFilter(request, response);} finally {// 后置处理:记录请求完成long duration = System.currentTimeMillis() - startTime;logRequestCompletion(httpRequest, duration);}}private void logRequestStart(HttpServletRequest request) {if (log.isDebugEnabled()) {log.debug("请求开始: {} {} from {}", request.getMethod(), request.getRequestURI(), request.getRemoteAddr());}}private void logRequestCompletion(HttpServletRequest request, long duration) {if (duration > 1000) {log.warn("慢请求: {} {} 耗时 {}ms", request.getMethod(), request.getRequestURI(), duration);} else if (log.isDebugEnabled()) {log.debug("请求完成: {} {} 耗时 {}ms", request.getMethod(), request.getRequestURI(), duration);}}
}
