过滤器
表单数据
浏览器只能通过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}
app1
和
app2
分别具有路径前缀
/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.ASYNC
和DispatcherType.ERROR
一起映射。如果使用Spring Framework的AbstractAnnotationConfigDispatcherServletInitializer
(请参阅Servlet配置),所有过滤器会自动注册到所有调度类型。但是,如果通过web.xml
或在Spring Boot中通过FilterRegistrationBean
注册过滤器,请确保除了DispatcherType.REQUEST
之外,还包括DispatcherType.ASYNC
和DispatcherType.ERROR
。
浅层ETag
ShallowEtagHeaderFilter
过滤器通过缓存写入响应的内容并从中计算MD5哈希来创建“浅层”ETag。下次客户端发送请求时,它会做同样的操作,但还会将计算出的值与If-None-Match
请求头进行比较,如果两者相等,则返回304(NOT_MODIFIED)。
这种策略节省了网络带宽,但并没有节省CPU,因为每个请求都必须计算完整的响应。改变状态的HTTP方法和其他HTTP条件请求头,如If-Match
和If-Unmodified-Since
不在此过滤器的范围之内。控制器级别的其他策略可以避免计算,并对HTTP条件请求有更广泛的支持。请参阅HTTP缓存。
此过滤器具有一个writeWeakETag
参数,该参数配置过滤器写入类似以下内容的弱ETags:W/"02a2d595e6ed9a0b24f027f2b63b134d6"
(如RFC 7232第2.3节中定义)。