过滤器

表单数据

浏览器只能通过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*()系列方法访问表单数据。

转发头

当请求通过诸如负载均衡器之类的代理时,主机、端口和方案可能会发生变化,这使得从客户端角度创建指向正确主机、端口和方案的链接成为一项挑战。

RFC 7239定义了Forwarded HTTP头,代理可以使用它来提供有关原始请求的信息。

非标准头部

还有其他非标准头部,包括 X-Forwarded-Host, X-Forwarded-Port, X-Forwarded-Proto, X-Forwarded-Ssl, 和 X-Forwarded-Prefix

X-Forwarded-Host

虽然不是标准的,X-Forwarded-Host: <host> 是一个事实标准头部,用于将原始主机信息传递给下游服务器。例如,如果一个请求 example.com/resource 被发送到一个代理,代理将请求转发到 localhost:8080/resource,那么可以发送一个头部 X-Forwarded-Host: example.com 来告知服务器原始主机是 example.com

X-Forwarded-Port

虽然不是标准的,X-Forwarded-Port: <port> 是一个事实标准头部,用于将原始端口信息传递给下游服务器。例如,如果一个请求 example.com/resource 被发送到一个代理,代理将请求转发到 localhost:8080/resource,那么可以发送一个头部 X-Forwarded-Port: 443 来告知服务器原始端口是 443

X-Forwarded-Proto

虽然不是标准的,X-Forwarded-Proto: (https|http) 是一个事实标准头部,用于将原始协议信息(例如 https / http)传递给下游服务器。例如,如果一个请求 example.com/resource 被发送到一个代理,代理将请求转发到 localhost:8080/resource,那么可以发送一个头部 X-Forwarded-Proto: https 来告知服务器原始协议是 https

X-Forwarded-Ssl

虽然不是标准的,X-Forwarded-Ssl: (on|off) 是一个事实标准头部,用于将原始协议信息(例如 https / http)传递给下游服务器。例如,如果一个请求 example.com/resource 被发送到一个代理,代理将请求转发到 localhost:8080/resource,那么可以发送一个头部 X-Forwarded-Ssl: on 来告知服务器原始协议是 https

X-Forwarded-Prefix

虽然不是标准的,X-Forwarded-Prefix: <prefix> 是一个事实标准头部,用于将原始 URL 路径前缀信息传递给下游服务器。

X-Forwarded-Prefix 的使用可能因部署场景而异,需要灵活以允许替换、移除或添加目标服务器的路径前缀。

场景 1: 覆盖路径前缀

https://example.com/api/{path} -> http://localhost:8080/app1/{path}
{path} 前的路径起始部分。对于代理,前缀是 /api,而对于服务器,前缀是 /app1。在这种情况下,代理可以发送 X-Forwarded-Prefix: /api 来使原始前缀 /api 覆盖服务器前缀 /app1

场景 2: 移除路径前缀

https://app1.example.com/{path} -> http://localhost:8080/app1/{path}
https://app2.example.com/{path} -> http://localhost:8080/app2/{path}
app1app2 分别具有路径前缀 /app1/app2。代理可以发送 X-Forwarded-Prefix: 来使空前缀覆盖服务器前缀 /app1/app2

场景 3: 插入路径前缀

https://example.com/api/app1/{path} -> http://localhost:8080/app1/{path}
/api/app1,服务器具有前缀 /app1。代理可以发送 X-Forwarded-Prefix: /api/app1 来使原始前缀 /api/app1 覆盖服务器前缀 /app1

ForwardedHeaderFilter

ForwardedHeaderFilter是一个Servlet过滤器,用于修改请求,以便a)根据Forwarded头部更改主机、端口和方案,b)删除这些头部以消除进一步影响。该过滤器依赖于包装请求,因此必须在其他过滤器之前排序,例如RequestContextFilter,这些过滤器应该使用修改后的请求而不是原始请求。

安全考虑

转发头部存在安全考虑,因为应用程序无法确定头部是由代理添加的,如预期的那样,还是由恶意客户端添加的。这就是为什么应该配置一个在信任边界上的代理来删除来自外部的不受信任的Forwarded头部。您还可以使用removeOnly=true配置ForwardedHeaderFilter,在这种情况下,它会删除但不使用这些头部。

调度程序类型

为了支持异步请求和错误分发,此过滤器应该与DispatcherType.ASYNCDispatcherType.ERROR一起映射。如果使用Spring Framework的AbstractAnnotationConfigDispatcherServletInitializer(请参阅Servlet配置),所有过滤器会自动注册到所有调度类型。但是,如果通过web.xml或在Spring Boot中通过FilterRegistrationBean注册过滤器,请确保除了DispatcherType.REQUEST之外,还包括DispatcherType.ASYNCDispatcherType.ERROR

浅层ETag

ShallowEtagHeaderFilter过滤器通过缓存写入响应的内容并从中计算MD5哈希来创建“浅层”ETag。下次客户端发送请求时,它会做同样的操作,但还会将计算出的值与If-None-Match请求头进行比较,如果两者相等,则返回304(NOT_MODIFIED)。

这种策略节省了网络带宽,但并没有节省CPU,因为每个请求都必须计算完整的响应。改变状态的HTTP方法和其他HTTP条件请求头,如If-MatchIf-Unmodified-Since不在此过滤器的范围之内。控制器级别的其他策略可以避免计算,并对HTTP条件请求有更广泛的支持。请参阅HTTP缓存

此过滤器具有一个writeWeakETag参数,该参数配置过滤器写入类似以下内容的弱ETags: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的过滤器链之前排序。

有关更多详细信息,请参阅CORSCORS过滤器部分。