一、拦截器 Interceptors
在 Java 配置中,可以注册拦截器以应用于传入请求,如以下示例所示:
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LocaleChangeInterceptor());
registry.addInterceptor(new ThemeChangeInterceptor()).addPathPatterns("/**").excludePathPatterns("/admin/**");
}
}
以下示例展示了如何在 XML 中实现相同的配置:
<mvc:interceptors>
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"/>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/admin/**"/>
<bean class="org.springframework.web.servlet.theme.ThemeChangeInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
Note
映射拦截器并不理想地适合作为安全层,因为可能与带注释的控制器路径匹配不匹配,该路径匹配还可以透明地匹配尾部斜杠和路径扩展以及其他路径匹配选项。其中许多选项已被弃用,但不匹配的可能性仍然存在。一般来说,我们建议使用 Spring Security,它包含一个专用的 MvcRequestMatcher 来与 Spring MVC 路径匹配保持一致,并且还具有一个安全防火墙,可以阻止 URL 路径中的许多不需要的字符。
二、过滤器 Filters
spring-web
模块提供了一些有用的过滤器:
2.1 Form Data 表格数据
浏览器只能通过 HTTP GET 或 HTTP POST 提交表单数据,但非浏览器客户端也可以使用 HTTP PUT、PATCH 和 DELETE。 Servlet API 需要 ServletRequest.getParameter*()
方法来支持仅针对 HTTP POST 的表单字段访问。
spring-web
模块提供 FormContentFilter
来拦截内容类型为 application/x-www-form-urlencoded
的 HTTP PUT、PATCH 和 DELETE 请求,从请求正文中读取表单数据,并包装 ServletRequest
以使表单数据可通过 ServletRequest.getParameter*()
系列方法使用。
2.2 Forwarded Headers 转发标头
当请求通过代理(例如负载均衡器)时,主机、端口和方案可能会发生变化,这使得从客户端角度创建指向正确主机、端口和方案的链接成为一项挑战。
RFC 7239 定义了 Forwarded
HTTP 标头,代理可使用该标头提供有关原始请求的信息。还有其他非标准标头,包括 X-Forwarded-Host
、 X-Forwarded-Port
、 X-Forwarded-Proto
、 X-Forwarded-Ssl
和 X-Forwarded-Prefix
.
ForwardedHeaderFilter
是一个 Servlet 过滤器,它修改请求,以便
a) 根据 Forwarded
标头更改主机、端口和方案,以及
b) 删除这些标头以消除进一步的影响。
该过滤器依赖于包装请求,因此它必须排在其他过滤器之前,例如 RequestContextFilter
,它应该适用于修改后的请求,而不是原始请求。
转发的标头存在安全考虑,因为应用程序无法知道标头是按预期由代理添加的,还是由恶意客户端添加的。这就是为什么应将信任边界处的代理配置为删除来自外部的不受信任的 Forwarded
标头。您还可以使用 removeOnly=true
配置 ForwardedHeaderFilter
,在这种情况下,它会删除但不使用标头。
为了支持异步请求和错误调度,此过滤器应与 DispatcherType.ASYNC
和 DispatcherType.ERROR
映射。如果使用 Spring Framework 的 AbstractAnnotationConfigDispatcherServletInitializer
(请参阅 Servlet 配置),所有过滤器都会自动为所有调度类型注册。但是,如果通过 web.xml
或在 Spring Boot 中通过 FilterRegistrationBean
注册过滤器,请确保包括 DispatcherType.ASYNC
和 DispatcherType.ERROR
,除了DispatcherType.REQUEST
。
2.3 Shallow ETag 浅层ETag
ShallowEtagHeaderFilter
过滤器通过缓存写入响应的内容并从中计算 MD5 哈希来创建“shallow”ETag。下次客户端发送时,它会执行相同的操作,但还会将计算值与 If-None-Match
请求标头进行比较,如果两者相等,则返回 304 (NOT_MODIFIED)。
此策略节省网络带宽,但不节省 CPU,因为必须为每个请求计算完整响应。状态更改 HTTP 方法和其他 HTTP 条件请求标头(例如 If-Match
和 If-Unmodified-Since
)不在此过滤器的范围内。控制器级别的其他策略可以避免计算并对 HTTP 条件请求提供更广泛的支持。请参阅 HTTP 缓存。
此过滤器有一个 writeWeakETag
参数,该参数将过滤器配置为写入类似于以下内容的弱 ETag: W/"02a2d595e6ed9a0b24f027f2b63b134d6"
(如 RFC 7232 第 2.3 节中所定义)。
为了支持异步请求,此过滤器必须与 DispatcherType.ASYNC
映射,以便过滤器可以延迟并成功生成 ETag 到最后一个异步调度的末尾。如果使用 Spring Framework 的 AbstractAnnotationConfigDispatcherServletInitializer
(请参阅 Servlet 配置),所有过滤器都会自动为所有调度类型注册。但是,如果通过 web.xml
或在 Spring Boot 中通过 FilterRegistrationBean
注册过滤器,请务必包含 DispatcherType.ASYNC
。
CORS 跨域资源共享
Spring MVC 通过控制器上的注释为 CORS 配置提供细粒度的支持。然而,当与 Spring Security 一起使用时,我们建议依赖内置的 CorsFilter
,它必须在 Spring Security 的过滤器链之前排序。
See the sections on and the for more details.
有关更多详细信息,请参阅有关 CORS 和 CORS Filter 的部分。
三、拦截器和过滤器的区别
Spring拦截器(Interceptor)和Servlet过滤器(Filter)是用于在Web应用程序中处理请求和响应的组件,但它们在实现、用途和范围等方面存在以下区别:
Spring拦截器:
- 范围: Spring拦截器是Spring框架内部提供的一种拦截机制,主要用于拦截Spring MVC框架中的Controller请求。
- 依赖性: Spring拦截器依赖于Spring框架,需要将应用构建成Spring MVC应用。
- 功能: 它允许在请求处理前和处理后执行预处理和后处理操作,如权限验证、日志记录、事务管理等。
- Controller访问: 拦截器可以访问Controller的方法和属性,还可以修改ModelAndView。
Servlet过滤器:
- 范围: Servlet过滤器是Servlet规范中定义的一种机制,独立于具体的框架,可以在Java Web应用的任何部分使用。
- 依赖性: 过滤器不依赖于Spring框架,适用于各种类型的Java Web应用。
- 功能: 它作用于Servlet容器级别,允许在请求到达Servlet之前或生成响应前对请求和响应进行处理,如字符编码、请求参数修改等。
- Controller访问: 过滤器不直接访问Controller的方法和属性,因为它在Servlet之前工作,与具体的Controller无关。
总结:
- 用途: Spring拦截器主要用于拦截Spring MVC框架中的Controller请求,并与Spring的AOP集成,提供更多高级功能。Servlet过滤器用于处理Servlet容器级别的请求和响应,不依赖于特定的框架。
- 依赖性: Spring拦截器依赖于Spring框架,而Servlet过滤器不依赖于任何特定框架,适用于各种Java Web应用。
- 访问能力: Spring拦截器可以直接访问Controller的方法和属性,而Servlet过滤器在Servlet之前工作,不能直接访问Controller。