|
对于最新的稳定版本,请使用 Spring Framework 7.0.6! |
过滤器
表单数据
浏览器只能通过HTTP GET或HTTP POST提交表单数据,但非浏览器客户端也可以使用HTTP PUT、PATCH和DELETE。Servlet API要求ServletRequest.getParameter*()方法仅支持HTTP POST的表单字段访问。
The 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-端口
尽管不是标准,X-Forwarded-Port: <port> 是一个事实上的标准头部,用于向下游服务器传递原始端口信息。例如,如果一个指向 example.com/resource 的请求被发送到代理服务器,该代理将请求转发至 localhost:8080/resource,那么可以发送一个值为 X-Forwarded-Port: 443 的头部,以告知服务器原始端口是 443。
X-Forwarded-Proto
尽管不是标准,X-Forwarded-Proto: (https|http)
实际上已成为一种用于向下游服务器传递原始协议(例如 http/https)
的标头。例如,如果将一个 example.com/resource 的请求发送到
代理服务器,并由该代理将请求转发至 localhost:8080/resource,则可以发送一个
X-Forwarded-Proto: https 的标头,以告知服务器原始协议是 https。
X-Forwarded-Ssl
尽管不是标准,X-Forwarded-Ssl: (on|off) 已成为一种事实上的标准头部,用于向下游服务器传递原始协议(例如 http / https)。例如,如果将 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。
|
此部署场景的一个常见情况是许可证按生产应用服务器计费,因此更倾向于在每台服务器上部署多个应用程序以降低费用。另一个原因是在同一台服务器上运行更多应用程序,以便共享服务器运行所需资源。 在这些场景中,应用程序需要一个非空的上下文根路径,因为同一台服务器上存在多个应用程序。然而,在公共 API 的 URL 路径中不应暴露此上下文路径,应用程序可以使用不同的子域名,从而带来如下好处:
|
场景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标头。你还可以配置ForwardedHeaderFilter使用removeOnly=true,在这种情况下,它会移除但不使用这些标头。
浅层ETag
The ShallowEtagHeaderFilter 过滤器通过缓存写入响应的内容并从中计算MD5哈希来创建一个“浅层”ETag。下次客户端发送请求时,它会执行相同的操作,但还会将计算出的值与If-None-Match 请求头进行比较,如果两者相等,则返回304(未修改)。
这种策略节省网络带宽但不节省CPU资源,因为每个请求都必须计算完整响应。
状态更改的HTTP方法以及其他HTTP条件请求标头(如If-Match和If-Unmodified-Since)不在该过滤器的处理范围内。控制器层面的其他策略可以避免计算,并对HTTP条件请求提供更广泛的支持。
请参阅HTTP缓存。
此过滤器有一个 writeWeakETag 参数,用于配置过滤器以写入类似于以下的弱 ETags: W/"02a2d595e6ed9a0b24f027f2b63b134d6"(如在
RFC 7232 第 2.3 节 中定义)。
CORS
Spring MVC 通过注解在控制器上提供了对 CORS 配置的细粒度支持。然而,当与 Spring Security 一起使用时,我们建议依赖内置的 CorsFilter,该组件必须在 Spring Security 的过滤器链之前进行排序。