Spring server.forward-headers-strategy NATIVE vs FRAMEWORK [英] Spring server.forward-headers-strategy NATIVE vs FRAMEWORK
问题描述
我最近将 spring boot 从 1.x 升级到 2.y 并面临这个问题,其中 hatoas 链接是使用 http
方案而不是 https
生成的.
后来发现用spring boot 2.2+,强制使用如下属性
server.forward-headers-strategy=NATIVE
可以有 NATIVE
或 FRAMEWORK
或 NONE
之一.
NONE
属性非常简单,它完全禁止使用前向标头.
但是没有关于 NATIVE
与 FRAMEWORK
的明确文档.我在很多地方都看到有人提到 NATIVE
在大多数情况下效果最好.但是当我们使用这些属性时,幕后到底发生了什么,并没有解释.
此处的文档 没有提供足够的信息让我在本机/框架之间进行选择.它所说的只是谁处理相应值的转发标头.Servlet 容器?还是Spring框架?但它又回到了第 1 方格.我应该让容器处理它吗?还是框架?我什么时候应该更喜欢一个?
我正在使用带有外部 tomcat 和 Hateoas
的 REST 网络应用程序来生成链接.
我如何决定是使用 NATIVE
还是 FRAMEWORK
属性?什么时候应该优先选择另一个,为什么?
我的springboot版本:2.4.6
我已经尝试过的参考:
- https://github.com/spring-projects/spring-boot/issues/18667
- https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.2-Release-Notes#deprecations-in-spring-boot-22
- https://docs.spring.io/spring-framework/docs/5.1.3.RELEASE/spring-framework-reference/web.html#filters-forwarded-headers
- https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#filters-forwarded-headers
我尝试了这两种解决方案,framework
对我有用,但在外部 tomcat 环境中不适用于 native
.我创建了一个带有嵌入式 tomcat 的新 Spring Boot Web 应用程序,并且 native
和 framework
都可以工作.
FRAMEWORK
FRAMEWORK
使用 Spring 支持处理转发的标头.例如,当 server.forward-headers-strategy=framework
.
ForwardedHeaderFilter
bean.@Bean@ConditionalOnMissingFilterBean(ForwardedHeaderFilter.class)@ConditionalOnProperty(value = "server.forward-headers-strategy", hasValue = "framework")public FilterRegistrationBeanforwardedHeaderFilter() {ForwardedHeaderFilter filter = new ForwardedHeaderFilter();FilterRegistrationBean注册 = 新的 FilterRegistrationBean<>(filter);registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC, DispatcherType.ERROR);注册.setOrder(Ordered.HIGHEST_PRECEDENCE);退货登记;}
ForwardedHeaderFilter
处理非标准标头 X-Forwarded-Host
、X-Forwarded-Port
、X-Forwarded-Proto
、X-Forwarded-Ssl
和 X-Forwarded-Prefix
.
原生
NATIVE
使用底层容器对转发标头的本机支持.底层容器指的是tomcat、jetty、netty等,比如Spring Boot自动配置的内嵌Tomcat处理非标准headerX-Forwarded-Host
、X-Forwarded-Port
、X-Forwarded-Proto
、X-Forwarded-Ssl
,但不是 X-Forwarded-Prefix
.>
X-Forwarded-Prefix
例如,API 网关运行在 localhost:8080
上,API 服务 sga-booking
运行在 localhost:20000
上.API网关路由/sga-booking
转发给api服务sga-booking
.对 localhost:8080/sga-booking
的请求包含标题:
forwarded = proto=http;host=localhost:8080";for=0:0:0:0:0:0:0:1%0:46706";x-forwarded-for = 0:0:0:0:0:0:0:1%0x-forwarded-proto = httpx-forwarded-prefix =/sga-bookingx 转发端口 = 8080x 转发主机 = 本地主机:8080主机 = 192.168.31.200:20000
当ForwardedHeaderFilter
处理包括X-Forwarded-Prefix
在内的转发标头时,生成的链接以localhost:8080/sga-booking
开头.如果未处理 X-Forwarded-Prefix
,则生成的链接以 localhost:8080
开头.
Spring Boot 自动配置的嵌入式 Tomcat
使用属性server.forward-headers-strategy=native
,方法org.springframework.boot.autoconfigure.web.embedded.TomcatWebServerFactoryCustomizer#customizeRemoteIpValve
使用属性 server.tomcat.remoteip
(org.springframework.boot.autoconfigure.web.ServerProperties.Tomcat.Remoteip
) 来处理转发的标头.注意 X-Forwarded-Prefix
没有被处理.
private void customRemoteIpValve(ConfigurableTomcatWebServerFactory factory) {Remoteip remoteIpProperties = this.serverProperties.getTomcat().getRemoteip();String protocolHeader = remoteIpProperties.getProtocolHeader();String remoteIpHeader = remoteIpProperties.getRemoteIpHeader();if (StringUtils.hasText(protocolHeader) || StringUtils.hasText(remoteIpHeader)||getOrDeduceUseForwardHeaders()) {RemoteIpValve 阀 = new RemoteIpValve();Valve.setProtocolHeader(StringUtils.hasLength(protocolHeader) ? protocolHeader : X-Forwarded-Proto");如果(StringUtils.hasLength(remoteIpHeader)){Valve.setRemoteIpHeader(remoteIpHeader);}Valve.setInternalProxies(remoteIpProperties.getInternalProxies());尝试 {//默认情况下 X-Forwarded-HostValve.setHostHeader(remoteIpProperties.getHostHeader());}抓住(NoSuchMethodError 前){//避免在 8.5.44 和 Tomcat 8.5 之前的 war 部署失败//9.0.23 之前的 Tomcat 9}//X-Forwarded-Port 默认Valve.setPortHeader(remoteIpProperties.getPortHeader());Valve.setProtocolHeaderHttpsValue(remoteIpProperties.getProtocolHeaderHttpsValue());factory.addEngineValves(阀门);}}
外部Tomcat
//抱歉,我毕业后好多年没玩过原版 Tomcat.以下Tomcat信息可能有误.
为了让外部 Tomcat 处理转发的 headers,就像 Spring Boot 配置的那样,我认为应该通过 add
RemoteIpValve
...<Valve className=org.apache.catalina.valves.RemoteIpValve"hostHeader="X-Forwarded-Host";portHeader="X-Forwarded-Port";.../>...上下文>
到Tomcat server.xml
?还是 context.xml
?在此处查找所有远程 ip Valve 属性.请注意,没有任何属性与 X-Forwarded-Prefix
相关.
Tomcat 过滤器 RemoteIpFilter
可能有类似的功能.我不知道它们的区别.
参考
I recently upgraded spring boot from 1.x to 2.y and was facing this issue where hateoas links were generated with http
scheme instead of https
.
Later I discovered that with spring boot 2.2+, it is mandatory to use the following property
server.forward-headers-strategy=NATIVE
which can have one of NATIVE
or FRAMEWORK
or NONE
.
NONE
property is pretty straight forward and it totally disables use of forward headers.
But there is no clear documentation on NATIVE
vs FRAMEWORK
. I've seen in many places it is mentioned that NATIVE
works the best in most cases. But there is no explanation on what exactly happens behind the scenes when we use these properties.
The documentation here doesn't give enough information for me to choose between Native/Framework. All it says is who handles the forwarded headers for corresponding values. Servlet container? or Spring framework? but it brings back to square 1. Should I let container handle it? or the framework? when Should I prefer one over the other?
I am using REST web application with external tomcat and Hateoas
for generating the links.
How do I decide whether to use NATIVE
or FRAMEWORK
property? When should one be preferred over the other and why?
My springboot version: 2.4.6
References I've already tried:
- https://github.com/spring-projects/spring-boot/issues/18667
- https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.2-Release-Notes#deprecations-in-spring-boot-22
- https://docs.spring.io/spring-framework/docs/5.1.3.RELEASE/spring-framework-reference/web.html#filters-forwarded-headers
- https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#filters-forwarded-headers
EDIT:
I tried both the solutions and framework
works for me but not native
in external tomcat environment. I created a new spring boot web application with embedded tomcat and both native
and framework
works.
FRAMEWORK
FRAMEWORK
uses Spring's support for handling forwarded headers. For example, Spring Boot auto creates an ForwardedHeaderFilter
bean for Spring MVC when server.forward-headers-strategy=framework
.
@Bean
@ConditionalOnMissingFilterBean(ForwardedHeaderFilter.class)
@ConditionalOnProperty(value = "server.forward-headers-strategy", havingValue = "framework")
public FilterRegistrationBean<ForwardedHeaderFilter> forwardedHeaderFilter() {
ForwardedHeaderFilter filter = new ForwardedHeaderFilter();
FilterRegistrationBean<ForwardedHeaderFilter> registration = new FilterRegistrationBean<>(filter);
registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC, DispatcherType.ERROR);
registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
return registration;
}
ForwardedHeaderFilter
handles non-standard headers X-Forwarded-Host
, X-Forwarded-Port
, X-Forwarded-Proto
, X-Forwarded-Ssl
, and X-Forwarded-Prefix
.
NATIVE
NATIVE
uses the underlying container's native support for forwarded headers. The underlying container means tomcat, jetty, netty, etc. For example, the embedded Tomcat which is auto-configured by Spring Boot handles non-standard headers X-Forwarded-Host
, X-Forwarded-Port
, X-Forwarded-Proto
, X-Forwarded-Ssl
, but not X-Forwarded-Prefix
.
X-Forwarded-Prefix
For example, API gateway runs on localhost:8080
and api service sga-booking
runs on localhost:20000
. API gateway route /sga-booking
is forwarded to api service sga-booking
.
The request to localhost:8080/sga-booking
contains headers:
forwarded = proto=http;host="localhost:8080";for="0:0:0:0:0:0:0:1%0:46706"
x-forwarded-for = 0:0:0:0:0:0:0:1%0
x-forwarded-proto = http
x-forwarded-prefix = /sga-booking
x-forwarded-port = 8080
x-forwarded-host = localhost:8080
host = 192.168.31.200:20000
When ForwardedHeaderFilter
handles forwarded headers including X-Forwarded-Prefix
, generated links starts with localhost:8080/sga-booking
. If X-Forwarded-Prefix
is not handled, generated links starts with localhost:8080
.
Embedded Tomcat auto-configured by Spring Boot
With property server.forward-headers-strategy=native
, method
org.springframework.boot.autoconfigure.web.embedded.TomcatWebServerFactoryCustomizer#customizeRemoteIpValve
configures a RemoteIpValve
with properties server.tomcat.remoteip
(org.springframework.boot.autoconfigure.web.ServerProperties.Tomcat.Remoteip
) to handle forwarded headers. Note that X-Forwarded-Prefix
is not handled.
private void customizeRemoteIpValve(ConfigurableTomcatWebServerFactory factory) {
Remoteip remoteIpProperties = this.serverProperties.getTomcat().getRemoteip();
String protocolHeader = remoteIpProperties.getProtocolHeader();
String remoteIpHeader = remoteIpProperties.getRemoteIpHeader();
if (StringUtils.hasText(protocolHeader) || StringUtils.hasText(remoteIpHeader)
|| getOrDeduceUseForwardHeaders()) {
RemoteIpValve valve = new RemoteIpValve();
valve.setProtocolHeader(StringUtils.hasLength(protocolHeader) ? protocolHeader : "X-Forwarded-Proto");
if (StringUtils.hasLength(remoteIpHeader)) {
valve.setRemoteIpHeader(remoteIpHeader);
}
valve.setInternalProxies(remoteIpProperties.getInternalProxies());
try {
// X-Forwarded-Host by default
valve.setHostHeader(remoteIpProperties.getHostHeader());
}
catch (NoSuchMethodError ex) {
// Avoid failure with war deployments to Tomcat 8.5 before 8.5.44 and
// Tomcat 9 before 9.0.23
}
// X-Forwarded-Port by default
valve.setPortHeader(remoteIpProperties.getPortHeader());
valve.setProtocolHeaderHttpsValue(remoteIpProperties.getProtocolHeaderHttpsValue());
factory.addEngineValves(valve);
}
}
External Tomcat
// Sorry I haven't play vanilla Tomcat for years after graduating from school. Below Tomcat info may be wrong.
To make external Tomcat handle forwarded headers, like what Spring Boot configures, I think A RemoteIpValve
should be configured by add
<Context>
...
<Valve className="org.apache.catalina.valves.RemoteIpValve"
hostHeader="X-Forwarded-Host"
portHeader="X-Forwarded-Port"
...
/>
...
</Context>
to Tomcat server.xml
? or context.xml
?
Find all remote ip valve attributes here. Note that no attribute is related to X-Forwarded-Prefix
.
Tomcat filter RemoteIpFilter
may have similar function. I don't know their difference.
Reference
- ServerProperties.ForwardHeadersStrategy Javadoc
- Spring Boot Reference Documentation about forwarded headers
- Spring HATEOAS forwarded header handling
这篇关于Spring server.forward-headers-strategy NATIVE vs FRAMEWORK的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!