Spring 服务器。forward-headers-strategy 本机与框架
Spring server.forward-headers-strategy NATIVE vs FRAMEWORK
我最近将 spring 引导从 1.x 升级到 2.y 并且遇到了这个问题,其中 hateoas 链接是使用 http
方案而不是 https
生成的.
后来发现用spring boot 2.2+,强制使用下面的属性
server.forward-headers-strategy=NATIVE
可以是NATIVE
或FRAMEWORK
或NONE
之一。
NONE
属性 非常简单,它完全禁止使用前向 headers.
但是没有关于NATIVE
vs FRAMEWORK
的明确文档。我在很多地方都看到 NATIVE
在大多数情况下效果最好。但是没有解释当我们使用这些属性时幕后到底发生了什么。
documentation here 没有提供足够的信息让我在 Native/Framework 之间做出选择。它所说的只是谁处理相应值的转发 headers。服务小程序容器?或 Spring 框架?但它又回到了方块 1。我应该让容器处理它吗?还是框架?什么时候我应该更喜欢一个?
我正在使用带有外部 tomcat 和 Hateoas
的 REST Web 应用程序来生成链接。
如何决定是使用 NATIVE
还是 FRAMEWORK
属性?什么时候应该优先选择一个,为什么?
我的spring引导版本: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 引导 Web 应用程序,并且 native
和 framework
都可以工作。
框架
FRAMEWORK
使用 Spring 对处理转发 headers 的支持。例如,Spring Boot auto 在 server.forward-headers-strategy=framework
.
时为 Spring MVC 创建一个 ForwardedHeaderFilter
bean
@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
处理 non-standard headers X-Forwarded-Host
、X-Forwarded-Port
、X-Forwarded-Proto
、X-Forwarded-Ssl
和 X-Forwarded-Prefix
.
原生
NATIVE
使用底层容器对转发 headers 的原生支持。底层容器指的是tomcat、jetty、netty等。比如内嵌的Tomcat就是auto-configured by Spring Boot handles non-standard headers X-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
的请求包含 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
当 ForwardedHeaderFilter
处理转发的 headers 包括 X-Forwarded-Prefix
时,生成的链接以 localhost:8080/sga-booking
开头。如果 X-Forwarded-Prefix
没有被处理,生成的链接以 localhost:8080
.
开头
通过 Spring 引导
嵌入 Tomcat auto-configured
用属性server.forward-headers-strategy=native
,方法
org.springframework.boot.autoconfigure.web.embedded.TomcatWebServerFactoryCustomizer#customizeRemoteIpValve
配置具有属性 server.tomcat.remoteip
(org.springframework.boot.autoconfigure.web.ServerProperties.Tomcat.Remoteip
) 的 RemoteIpValve
来处理转发的 headers。注意X-Forwarded-Prefix
没有处理。
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);
}
}
外部Tomcat
// 抱歉我毕业后好多年没玩原版了Tomcat。 Tomcat以下信息可能有误。
要使外部 Tomcat 句柄转发 headers,就像 Spring Boot 配置的那样,我认为 A RemoteIpValve
应该通过 add
配置
<Context>
...
<Valve className="org.apache.catalina.valves.RemoteIpValve"
hostHeader="X-Forwarded-Host"
portHeader="X-Forwarded-Port"
...
/>
...
</Context>
到Tomcatserver.xml
?或 context.xml
?
查找所有远程 ip 阀属性 here。请注意,没有属性与 X-Forwarded-Prefix
.
相关
Tomcat 过滤器 RemoteIpFilter
可能有类似的功能。我不知道他们的区别。
参考
我最近将 spring 引导从 1.x 升级到 2.y 并且遇到了这个问题,其中 hateoas 链接是使用 http
方案而不是 https
生成的.
后来发现用spring boot 2.2+,强制使用下面的属性
server.forward-headers-strategy=NATIVE
可以是NATIVE
或FRAMEWORK
或NONE
之一。
NONE
属性 非常简单,它完全禁止使用前向 headers.
但是没有关于NATIVE
vs FRAMEWORK
的明确文档。我在很多地方都看到 NATIVE
在大多数情况下效果最好。但是没有解释当我们使用这些属性时幕后到底发生了什么。
documentation here 没有提供足够的信息让我在 Native/Framework 之间做出选择。它所说的只是谁处理相应值的转发 headers。服务小程序容器?或 Spring 框架?但它又回到了方块 1。我应该让容器处理它吗?还是框架?什么时候我应该更喜欢一个?
我正在使用带有外部 tomcat 和 Hateoas
的 REST Web 应用程序来生成链接。
如何决定是使用 NATIVE
还是 FRAMEWORK
属性?什么时候应该优先选择一个,为什么?
我的spring引导版本: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 引导 Web 应用程序,并且 native
和 framework
都可以工作。
框架
FRAMEWORK
使用 Spring 对处理转发 headers 的支持。例如,Spring Boot auto 在 server.forward-headers-strategy=framework
.
ForwardedHeaderFilter
bean
@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
处理 non-standard headers X-Forwarded-Host
、X-Forwarded-Port
、X-Forwarded-Proto
、X-Forwarded-Ssl
和 X-Forwarded-Prefix
.
原生
NATIVE
使用底层容器对转发 headers 的原生支持。底层容器指的是tomcat、jetty、netty等。比如内嵌的Tomcat就是auto-configured by Spring Boot handles non-standard headers X-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
的请求包含 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
当 ForwardedHeaderFilter
处理转发的 headers 包括 X-Forwarded-Prefix
时,生成的链接以 localhost:8080/sga-booking
开头。如果 X-Forwarded-Prefix
没有被处理,生成的链接以 localhost:8080
.
通过 Spring 引导
嵌入 Tomcat auto-configured用属性server.forward-headers-strategy=native
,方法
org.springframework.boot.autoconfigure.web.embedded.TomcatWebServerFactoryCustomizer#customizeRemoteIpValve
配置具有属性 server.tomcat.remoteip
(org.springframework.boot.autoconfigure.web.ServerProperties.Tomcat.Remoteip
) 的 RemoteIpValve
来处理转发的 headers。注意X-Forwarded-Prefix
没有处理。
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);
}
}
外部Tomcat
// 抱歉我毕业后好多年没玩原版了Tomcat。 Tomcat以下信息可能有误。
要使外部 Tomcat 句柄转发 headers,就像 Spring Boot 配置的那样,我认为 A RemoteIpValve
应该通过 add
<Context>
...
<Valve className="org.apache.catalina.valves.RemoteIpValve"
hostHeader="X-Forwarded-Host"
portHeader="X-Forwarded-Port"
...
/>
...
</Context>
到Tomcatserver.xml
?或 context.xml
?
查找所有远程 ip 阀属性 here。请注意,没有属性与 X-Forwarded-Prefix
.
Tomcat 过滤器 RemoteIpFilter
可能有类似的功能。我不知道他们的区别。