Filters
-
Form 数据:允许使用非标准 HTTP 方法(如 PUT、DELETE)提交表单数据,使其更易于与非浏览器客户端交互。
-
转发标头:修改传入请求,允许应用程序根据转发代理提供的标头设置主机、端口和方案。
-
浅层 ETag:创建一个“浅层”ETag,节省带宽,但没有节省 CPU。它通过将响应内容的 MD5 哈希与客户端提供的 If-None-Match 请求标头进行比较来检测缓存。
-
CORS:在控制器级别提供细粒度的 CORS 配置,但与 Spring Security 一起使用时,建议使用内置的 CorsFilter 来简化集成。
spring-web
模块提供了一些有用的过滤器:
The spring-web
module provides some useful filters:
Form Data
浏览器只能通过 HTTP GET 或 HTTP POST 提交表单数据,但非浏览器客户端还可以使用 HTTP PUT、PATCH 和 DELETE。Servlet API 要求 `ServletRequest.getParameter*()`方法仅针对 HTTP POST 支持表单字段访问。
Browsers can submit form data only through HTTP GET or HTTP POST but non-browser clients can also
use HTTP PUT, PATCH, and DELETE. The Servlet API requires ServletRequest.getParameter*()
methods to support form field access only for HTTP POST.
spring-web
模块提供 FormContentFilter
来拦截具有 application/x-www-form-urlencoded
内容类型的 HTTP PUT、PATCH 和 DELETE 请求,从请求主体中读取表单数据,并封装 ServletRequest
以通过 ServletRequest.getParameter*()
系列方法提供表单数据。
The spring-web
module provides FormContentFilter
to intercept HTTP PUT, PATCH, and DELETE
requests with a content type of application/x-www-form-urlencoded
, read the form data from
the body of the request, and wrap the ServletRequest
to make the form data
available through the ServletRequest.getParameter*()
family of methods.
Non-standard Headers
当请求经过负载平衡器等代理时,主机、端口和协议可能会更改,而且这对于从客户端角度创建指向正确的主机、端口和协议的链接来说是个挑战。
As a request goes through proxies such as load balancers the host, port, and scheme may change, and that makes it a challenge to create links that point to the correct host, port, and scheme from a client perspective.
RFC 7239 定义了 Forwarded
HTTP 标头,代理可以使用它来提供有关原始请求的信息。
RFC 7239 defines the Forwarded
HTTP header
that proxies can use to provide information about the original request.
Non-standard Headers
也有一些其他非标准头,包括 X-Forwarded-Host
、X-Forwarded-Port
、X-Forwarded-Proto
、X-Forwarded-Ssl
和 X-Forwarded-Prefix
。
There are other non-standard headers, too, including X-Forwarded-Host
, X-Forwarded-Port
,
X-Forwarded-Proto
, X-Forwarded-Ssl
, and X-Forwarded-Prefix
.
X-Forwarded-Host
虽然不是标准,但 X-Forwarded-Host: <host>
是一个事实上的标准标头,用于将原始主机传达给下游服务器。例如,如果将 https://example.com/resource
的请求发送到将请求转发给 http://localhost:8080/resource
的代理,那么可以发送 X-Forwarded-Host: example.com
的标头以告知服务器原始主机是 example.com
。
While not standard, X-Forwarded-Host: <host>
is a de-facto standard header that is used to communicate the original host to a
downstream server. For example, if a request of https://example.com/resource
is sent to
a proxy which forwards the request to http://localhost:8080/resource
, then a header of
X-Forwarded-Host: example.com
can be sent to inform the server that the original host was example.com
.
X-Forwarded-Port
虽然不是标准,X-Forwarded-Port: <port>`是用于向服务器传递原始端口的实际标准头信息。例如,如果发送`https://example.com/resource`请求到将请求转发到`http://localhost:8080/resource`的代理,那么可以发送`X-Forwarded-Port: 443`头信息来通知服务器原始端口为`443
。
While not standard, X-Forwarded-Port: <port>
is a de-facto standard header that is used to
communicate the original port to a downstream server. For example, if a request of
https://example.com/resource
is sent to a proxy which forwards the request to
http://localhost:8080/resource
, then a header of X-Forwarded-Port: 443
can be sent
to inform the server that the original port was 443
.
X-Forwarded-Proto
虽然不是标准,但 X-Forwarded-Proto: (https|http)
是一个事实上的标准标头,用于将原始协议(例如 https/https
)传达给下游服务器。例如,如果将 https://example.com/resource
的请求发送到将请求转发给 http://localhost:8080/resource
的代理,那么可以发送 X-Forwarded-Proto: https
的标头以告知服务器原始协议是 https
。
While not standard, X-Forwarded-Proto: (https|http)
is a de-facto standard header that is used to communicate the original protocol (e.g. https / https)
to a downstream server. For example, if a request of https://example.com/resource
is sent to
a proxy which forwards the request to http://localhost:8080/resource
, then a header of
X-Forwarded-Proto: https
can be sent to inform the server that the original protocol was https
.
X-Forwarded-Ssl
虽然不是标准,X-Forwarded-Ssl: (on|off)`是用于向服务器传递原始协议(例如https/https)的实际标准头信息。例如,如果发送`https://example.com/resource`请求到将请求转发到`http://localhost:8080/resource`的代理,那么可以发送`X-Forwarded-Ssl: on`头信息来通知服务器原始协议为`https
。
While not standard, X-Forwarded-Ssl: (on|off)
is a de-facto standard header that is used to communicate the
original protocol (e.g. https / https) to a downstream server. For example, if a request of
https://example.com/resource
is sent to a proxy which forwards the request to
http://localhost:8080/resource
, then a header of X-Forwarded-Ssl: on
to inform the server that the
original protocol was https
.
X-Forwarded-Prefix
虽然不是标准,但 X-Forwarded-Prefix: <prefix>
是一个事实上的标准标头,用于将原始 URL 路径前缀传达给下游服务器。
While not standard, X-Forwarded-Prefix: <prefix>
is a de-facto standard header that is used to communicate the original URL path prefix to a
downstream server.
`X-Forwarded-Prefix`的使用可因部署情况而异,并且需要灵活才能替换、删除或前置目标服务器的路径前缀。
Use of X-Forwarded-Prefix
can vary by deployment scenario, and needs to be flexible to
allow replacing, removing, or prepending the path prefix of the target server.
场景1:覆盖路径前缀
Scenario 1: Override path prefix
https://example.com/api/{path} -> http://localhost:8080/app1/{path}
前缀是捕获组 {path}
之前的路径开头。对于代理,前缀是`/api`,而对于服务器,前缀是`/app1`。在这种情况下,代理可以发送`X-Forwarded-Prefix: /api`,让原始前缀`/api`覆盖服务器前缀`/app1`。
The prefix is the start of the path before the capture group {path}
. For the proxy,
the prefix is /api
while for the server the prefix is /app1
. In this case, the proxy
can send X-Forwarded-Prefix: /api
to have the original prefix /api
override the
server prefix /app1
.
场景2:删除路径前缀
Scenario 2: Remove path prefix
有时,应用程序可能希望删除前缀。例如,考虑以下代理到服务器的映射:
At times, an application may want to have the prefix removed. For example, consider the following proxy to server mapping:
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`。
The proxy has no prefix, while applications app1
and app2
have path prefixes
/app1
and /app2
respectively. The proxy can send X-Forwarded-Prefix: ` to
have the empty prefix override server prefixes `/app1
and /app2
.
这种情况的常见部署场景是,每个生产应用程序服务器都要付费获得许可证,最好在每个服务器上部属于多个应用程序以减少费用。另一个原因是在同一服务器上运行更多应用程序,以便共享服务器运行所需的资源。 A common case for this deployment scenario is where licenses are paid per production application server, and it is preferable to deploy multiple applications per server to reduce fees. Another reason is to run more applications on the same server in order to share the resources required by the server to run. 在这些场景中,应用程序需要一个非空上下文根,因为同一服务器上存在多个应用程序。然而,这不应在应用程序可能使用不同子域的公开API 的URL路径中可见,这些子域提供的优点包括: In these scenarios, applications need a non-empty context root because there are multiple applications on the same server. However, this should not be visible in URL paths of the public API where applications may use different subdomains that provides benefits such as:
|
场景3:插入路径前缀
Scenario 3: Insert path prefix
在其他情况下,可能需要前置前缀。例如,考虑以下代理到服务器的映射:
In other cases, it may be necessary to prepend a prefix. For example, consider the following proxy to server mapping:
https://example.com/api/app1/{path} -> http://localhost:8080/app1/{path}
在这种情况下,代理的前缀为`/api/app1`,服务器的前缀为`/app1`。代理可以发送`X-Forwarded-Prefix: /api/app1`,让原始前缀`/api/app1`覆盖服务器前缀`/app1`。
In this case, the proxy has a prefix of /api/app1
and the server has a prefix of
/app1
. The proxy can send X-Forwarded-Prefix: /api/app1
to have the original prefix
/api/app1
override the server prefix /app1
.
ForwardedHeaderFilter
ForwardedHeaderFilter
是一种 Servlet 过滤器,用于根据 Forwarded
标头来修改请求(a)更改主机、端口和方案,以及(b)移除这些标头以消除进一步的影响。该过滤器依赖于封装请求,因此必须排在其他过滤器之前,例如 RequestContextFilter
,而该过滤器应该使用修改后的请求,而不是使用原始请求。
ForwardedHeaderFilter
is a Servlet filter that modifies the request in order to
a) change the host, port, and scheme based on Forwarded
headers, and b) to remove those
headers to eliminate further impact. The filter relies on wrapping the request, and
therefore it must be ordered ahead of other filters, such as RequestContextFilter
, that
should work with the modified and not the original request.
Security Considerations
需要考虑转发标头所涉及的安全因素,因为应用程序无法得知这些标头是由代理按照预期添加的,还是由恶意客户端添加的。这就是位于信任边界处的代理应该被配置为移除从外部传入的不可信 Forwarded
标头的缘故。您还可以使用 removeOnly=true
来配置 ForwardedHeaderFilter
,在这种情况下,它会移除标头,但不会使用这些标头。
There are security considerations for forwarded headers since an application cannot know
if the headers were added by a proxy, as intended, or by a malicious client. This is why
a proxy at the boundary of trust should be configured to remove untrusted Forwarded
headers that come from the outside. You can also configure the ForwardedHeaderFilter
with removeOnly=true
, in which case it removes but does not use the headers.
Dispatcher Types
为了支持asynchronous requests和错误调度,此过滤器应当与`DispatcherType.ASYNC`和`DispatcherType.ERROR`一起映射。如果使用 Spring Framework 的`AbstractAnnotationConfigDispatcherServletInitializer`(请参阅Servlet Config),则会为所有调度类型自动注册所有过滤器。然而,如果通过`web.xml`或在 Spring Boot 中通过`FilterRegistrationBean`注册过滤器,请务必在`DispatcherType.REQUEST`之外包含`DispatcherType.ASYNC`和`DispatcherType.ERROR`。
In order to support asynchronous requests and error dispatches this
filter should be mapped with DispatcherType.ASYNC
and also DispatcherType.ERROR
.
If using Spring Framework’s AbstractAnnotationConfigDispatcherServletInitializer
(see Servlet Config) all filters are automatically registered for all dispatch
types. However if registering the filter via web.xml
or in Spring Boot via a
FilterRegistrationBean
be sure to include DispatcherType.ASYNC
and
DispatcherType.ERROR
in addition to DispatcherType.REQUEST
.
Shallow ETag
ShallowEtagHeaderFilter
过滤器创建一个“浅层”Etag,方法是缓存写入响应的内容并由此计算一个 MD5 哈希。下次客户端发送请求时,它会执行相同的操作,但它还会将计算出的值与 If-None-Match
请求标头进行比较,并且这两个值相同时返回 304(NOT_MODIFIED)。
The ShallowEtagHeaderFilter
filter creates a “shallow” ETag by caching the content
written to the response and computing an MD5 hash from it. The next time a client sends,
it does the same, but it also compares the computed value against the If-None-Match
request header and, if the two are equal, returns a 304 (NOT_MODIFIED).
此策略可节省网络带宽,但不能节省 CPU,因为必须针对每个请求计算出完整响应。 状态更改 HTTP 方法以及其他 HTTP 条件请求头(如`If-Match`和`If-Unmodified-Since`)超出了此过滤器的范围。控制器级别的其他策略可以避免计算,并且对 HTTP 条件请求的兼容性更强。请参阅HTTP Caching。
This strategy saves network bandwidth but not CPU, as the full response must be computed for each request.
State-changing HTTP methods and other HTTP conditional request headers such as If-Match
and
If-Unmodified-Since
are outside the scope of this filter. Other strategies at the controller level
can avoid the computation and have a broader support for HTTP conditional requests.
See HTTP Caching.
此过滤器有一个`writeWeakETag`参数,用于将过滤器配置为写入类似于以下内容的弱 ETag:W/"02a2d595e6ed9a0b24f027f2b63b134d6"
(如https://datatracker.ietf.org/doc/html/rfc7232#section-2.3[RFC 7232 第 2.3 节]中定义)。
This filter has a writeWeakETag
parameter that configures the filter to write weak ETags
similar to the following: W/"02a2d595e6ed9a0b24f027f2b63b134d6"
(as defined in
RFC 7232 Section 2.3).
为了支持异步请求,此过滤器必须映射到DispatcherType.ASYNC,以便过滤器能够延迟并将ETag成功生成到最后一个异步分发末尾。如果使用Spring Framework的AbstractAnnotationConfigDispatcherServletInitializer(请参阅Servlet配置),则所有过滤器均会自动为所有分发类型注册。但如果通过web.xml或在Spring Boot中通过FilterRegistrationBean注册过滤器,请务必包括DispatcherType.ASYNC。
In order to support asynchronous requests this filter must be mapped
with DispatcherType.ASYNC
so that the filter can delay and successfully generate an
ETag to the end of the last async dispatch. If using Spring Framework’s
AbstractAnnotationConfigDispatcherServletInitializer
(see Servlet Config)
all filters are automatically registered for all dispatch types. However if registering
the filter via web.xml
or in Spring Boot via a FilterRegistrationBean
be sure to include
DispatcherType.ASYNC
.
CORS
Spring MVC通过控制器注释提供了细粒度的CORS配置支持。但与Spring Security一起使用时,我们建议依赖内置的CorsFilter,该过滤器必须早于Spring Security的过滤器链进行排序。
Spring MVC provides fine-grained support for CORS configuration through annotations on
controllers. However, when used with Spring Security, we advise relying on the built-in
CorsFilter
that must be ordered ahead of Spring Security’s chain of filters.
请参阅有关 CORS 和 CORS Filter 的部分,了解更多详情。
See the sections on CORS and the CORS Filter for more details.