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

可以是NATIVEFRAMEWORKNONE之一。

NONE 属性 非常简单,它完全禁止使用前向 headers.

但是没有关于NATIVE vs FRAMEWORK 的明确文档。我在很多地方都看到 NATIVE 在大多数情况下效果最好。但是没有解释当我们使用这些属性时幕后到底发生了什么。

documentation here 没有提供足够的信息让我在 Native/Framework 之间做出选择。它所说的只是谁处理相应值的转发 headers。服务小程序容器?或 Spring 框架?但它又回到了方块 1。我应该让容器处理它吗?还是框架?什么时候我应该更喜欢一个?

我正在使用带有外部 tomcat 和 Hateoas 的 REST Web 应用程序来生成链接。

如何决定是使用 NATIVE 还是 FRAMEWORK 属性?什么时候应该优先选择一个,为什么?

我的spring引导版本:2.4.6

我已经尝试过的参考资料:

编辑:

我尝试了两种解决方案,framework 对我有效,但在外部 tomcat 环境中 native 无效。我创建了一个带有嵌入式 tomcat 的新 spring 引导 Web 应用程序,并且 nativeframework 都可以工作。

框架

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-HostX-Forwarded-PortX-Forwarded-ProtoX-Forwarded-SslX-Forwarded-Prefix.

原生

NATIVE 使用底层容器对转发 headers 的原生支持。底层容器指的是tomcat、jetty、netty等。比如内嵌的Tomcat就是auto-configured by Spring Boot handles non-standard headers X-Forwarded-HostX-Forwarded-PortX-Forwarded-ProtoX-Forwarded-Ssl,但不是 X-Forwarded-Prefix

X-Forwarded-Prefix

例如,API 网关在 localhost:8080 上运行,api 服务 sga-bookinglocalhost: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 可能有类似的功能。我不知道他们的区别。

参考