Skip to content

过滤器 (Filters)

在 Servlet API 中,你可以添加 jakarta.servlet.Filter 来在过滤器链的其他部分和目标 Servlet 处理的前后应用拦截逻辑。

spring-web 模块提供了许多内置的 Filter 实现:

内置过滤器实现

1. 表单数据 (Form Data)

浏览器通常只能通过 HTTP GET 或 HTTP POST 提交表单数据。FormContentFilter 可以拦截 HTTP PUT、PATCH 和 DELETE 请求(Content-Type 为 application/x-www-form-urlencoded),读取主体中的表单数据,并将其包装,使这些数据可以通过 ServletRequest.getParameter*() 方法族获取。

2. Forwarded 标头 (Forwarded Headers)

当请求通过负载均衡器等代理时,主机、端口和方案可能会发生变化。ForwardedHeaderFilter 会根据 Forwarded 标头(RFC 7239)或非标准标头(如 X-Forwarded-HostX-Forwarded-PortX-Forwarded-Proto 等)修改请求,并随后移除这些标头。

安全考虑

转发标头存在安全风险,因为应用无法确定标头是来自真实的代理还是恶意客户端。处于信任边界的代理应配置为移除不受信任的外部标头。

3. Shallow ETag

ShallowEtagHeaderFilter 通过缓存写入响应的内容并计算 MD5 哈希来创建“浅层”ETag。下次客户端发送请求时,它会比较计算出的值与 If-None-Match 标头,如果相等则返回 304 (NOT_MODIFIED)。

  • 注意:这种策略节省带宽但不节省 CPU,因为每次请求仍需计算完整的响应。

4. CORS

虽然 Spring MVC 提供了控制器级别的 CORS 配置,但建议在使用 Spring Security 时,依靠内置的 CorsFilter,该过滤器必须排在 Spring Security 过滤器链之前。

5. URL 处理器 (URL Handler)

UrlHandlerFilter 用于处理 URL 路径末尾的斜杠。例如,它可以将 /blog/post/ 重定向到 /blog/post,或者将其包装为不带斜杠的请求继续处理。

基类实现

  • GenericFilterBean:将 Filter 配置为 Spring Bean 的基类,集成了 Spring ApplicationContext 的生命周期。
  • OncePerRequestFilter:扩展自 GenericFilterBean,确保在一次请求调度中只执行一次(例如忽略 FORWARD 调度产生的重复执行),并提供对 ASYNCERROR 调度的控制。

补充教学

1. Filter vs OncePerRequestFilter

在传统的 Servlet 编程中,一个请求可能会经过多次 dispatch(重定向、转发、错误处理等)。

  • 普通 Filter:可能会在一次完整的“用户请求”中被触发多次。
  • OncePerRequestFilter:Spring 提供的神器。它保证在同一个线程、同一个请求生命周期内,该过滤器的逻辑只会被执行一次。这在处理权限校验、审计日志时非常有用。

2. Spring Boot 中注册 Filter 的三种方式

  1. @Component:直接声明为 Bean,Spring Boot 会自动扫描并按默认顺序加入链。
  2. FilterRegistrationBean最推荐。可以精确控制 urlPatterns(映射路径)、执行 order(顺序)以及 dispatcherTypes
  3. @WebFilter + @ServletComponentScan:原生的 Servlet 注解方式。

3. ForwardedHeaderFilter 的重要性

如果你在做 OAuth2 登录或生成绝对路径链接,且你的应用部署在 Nginx/F5 后面,务必开启这个过滤器。否则,Spring 生成的重定向地址可能会是你内网的 http://10.0.0.1:8080/login,而不是外网的 https://example.com/login

Based on Spring Framework.