【问题标题】:Spring Boot with embedded Tomcat behind Apache proxy在 Apache 代理后面嵌入 Tomcat 的 Spring Boot
【发布时间】:2014-10-16 20:04:10
【问题描述】:

我们有一个带有嵌入式 Tomcat 的 Spring Boot (Spring MVC) 应用,位于 Apache SSL 代理后面的专用应用服务器上。

代理服务器上的SSL端口是4433,转发到appserver上的8080端口。

所以代理服务器的 URL 是这样转发的:

https://proxyserver:4433/appname   >>forward>>   http://appserver:8080/

在没有代理的情况下运行时,首先发生的是
Spring Security 重定向请求,如:

http://appserver:8080/   >>redirect>>   http://appserver:8080/login

通过扩展WebSecurityConfigurerAdapter来显示登录表单

  ...
  httpSecurity.formLogin().loginPage("/login") ...
  ...

没有代理也可以正常工作,但是使用代理需要更改重定向,
所以 Spring 应该改为重定向到相应的代理 URL,例如:

http://appserver:8080/   >>redirect>>   https://proxyserver:4433/appname/login

但还没有成功。

我正在尝试应用此解决方案: 59.8 Use Tomcat behind a front-end proxy server

我们在 Apache 中配置了mod_proxy,并验证它发送了预期的标头:

X-Forwarded-For: xxx.xxx.xxx.xxx
X-Forwarded-Host: proxyserver
X-Forwarded-Port: 4433
X-Forwarded-Proto: https

应用程序以参数启动:

export ARG1='-Dserver.tomcat.protocol-header=x-forwarded-proto' 
export ARG2='-Dserver.tomcat.remote-ip-header=x-forwarded-for'
java $ARG1 $ARG2 -jar webapp.jar

重定向仍然不起作用。

它将继续在本地重定向到客户端无法使用的http://appserver:8080/login

我们还需要做些什么来让这个场景发挥作用吗?


更新

另外,我担心代理 URL 中的“/appname”部分。在应用服务器上,应用程序植根于“/”。当通过代理时,应如何指示 Spring 将“/appname”包含在发送回客户端的所有 URL 中?

【问题讨论】:

  • 有点令人不安的是,关于 JVM 属性:server.tomcat.protocol-header, server.tomcat.remote-ip-header ..... at docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/… 它们在最后一个之后用下划线编写点,而docs.spring.io/spring-boot/docs/current/reference/html/… 则用破折号书写。哪个是对的?我尝试了这两种变体,但没有解决上述问题。
  • 应该是下划线server.tomcat.remote_ip_header=x-forwarded-forserver.tomcat.protocol_header=x-forwarded-proto
  • Spring boot 对属性使用宽松的绑定,因此它们是下划线还是破折号甚至是圆点都可能无关紧要

标签: spring tomcat spring-security reverse-proxy spring-boot


【解决方案1】:

前几天我也遇到了同样的问题。在对 Spring Boot 1.3 进行了一些调试后,我找到了以下解决方案。

1.您必须在 Apache 代理上设置标头:

<VirtualHost *:443>
    ServerName www.myapp.org
    ProxyPass / http://127.0.0.1:8080/
    RequestHeader set X-Forwarded-Proto https
    RequestHeader set X-Forwarded-Port 443
    ProxyPreserveHost On
    ... (SSL directives omitted for readability)
</VirtualHost>

2. 您必须告诉您的 Spring Boot 应用程序使用这些标头。所以把下面这行放在你的 application.properties 中(或者 Spring Boots 理解属性的任何其他地方):

server.use-forward-headers=true

如果您正确执行这两件事,您的应用程序发送的每个重定向将不会转到http://127.0.0.1:8080/[path],而是自动转到https://www.myapp.com/[path]

更新 1。 关于此主题的文档是 here。您至少应该阅读它以了解属性 server.tomcat.internal-proxies,它定义了可信任的代理服务器的 IP 地址范围。

2021 年更新文档已移至 here。 Spring Boot 配置现在有点不同了。

【讨论】:

  • server.use-forward-headers=true 适用于谷歌云负载均衡器
  • 该死的,你救了我。 ProxyPreserveHost On 非常重要... :) 如果您忽略它,您的应用程序将重定向到 localhost,而不是使用规范地址保留原始主机。
  • 嘿@Igor Mukhin 链接坏了你能更新链接吗?我真的很想了解更多关于这方面的内容。
  • @Norbert 已更新,但您实际上可以自己完成。
  • 如果我只是将 Apache Web 服务器上的端口 80 用于 HTTP,该怎么办?我将端口 80 和 http 用于转发的标头,但没有任何效果
【解决方案2】:

您的代理看起来不错,后端应用程序看起来也不错,但它似乎没有看到 RemoteIpValve 修改后的请求。 RemoteIpValve 的默认行为包括代理 IP 地址的模式匹配(作为安全检查),它只修改它认为来自有效代理的请求。 Spring Boot 中的模式默认为一组众所周知的内部 IP 地址,例如 10.*.*.*192.168.*.*,因此如果您的代理不在其中一个上,您需要显式配置它,例如

server.tomcat.internal-proxies=172\\.17\\.\\d{1,3}\\.\\d{1,3}|127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}

(使用属性文件格式,这意味着您必须对反斜杠进行双重转义)。

如果你设置了,你可以在RemoteIpValve看到发生了什么

logging.level.org.apache.catalina.valves.RemoteIpValve=DEBUG

或在其中设置断点。

【讨论】:

    【解决方案3】:

    此问题的典型解决方案是让代理处理任何所需的重写。例如,在 Apache 中,您可以使用 rewrite_module 和/或 headers_module 来更正标头。再举一个例子,Nginx 在配置上游服务器后会自动为您处理这种情况和其他类似情况。

    回应cmets:

    remote_ip_header 和 protocol_header spring boot 配置值是什么?

    让我们暂时忘记 Spring Boot。 Tomcat 是嵌入式 servlet 容器,具有称为 RemoteIpValve 的阀门。这个阀门是 Apache remotip_module 的一个端口。这个阀门的主要目的是为了“授权和记录的目的”将“发起请求的用户代理作为原始用户代理”。要使用此阀门,需要对其进行配置。

    请查找有关此阀门的更多信息here

    Spring Boot 通过 server.tomcat.remote_ip_header 和 server.tomcat.protocol_header 属性方便地支持通过 application.properties 配置此 Valve。

    【讨论】:

    • 好的...但我更愿意让spring-documented支持工作:docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/… ---这些参数应该做什么?我没有看到它们有任何影响,但如果我能让它工作,这似乎是一个更简单的解决方案......不过我想我应该澄清我的问题......
    • 将 302 代码重写为 200 并将位置从 http 重写为 https?不,通过代理重写不是解决方案。它应该由 spring boot 中的 app.properties 处理,但它不是(至少对我而言)。
    • 我仍然遇到同样的问题。你修好罗普了吗?如果有,可以分享给我吗?
    • URL 重写很麻烦。如果使用 JSP 和/或其他一些模板引擎,重写模块会使您的生活陷入噩梦。应尽可能使用 tomcat 连接器的设置(行“sceme”、“proxyName”、“proxyPort”等),
    【解决方案4】:

    您可以配置几个与此相关的属性。 application.yaml 例子:

    server:
      forward-headers-strategy: native
      tomcat:
        use-relative-redirects: true
        protocol-header: x-forwarded-proto
        remote-ip-header: x-forwarded-for
    

    设置server.forward-headers-strategy: native 是替换已弃用的server.use-forward-headers:true

    【讨论】:

    【解决方案5】:

    我使用 haproxy 作为负载均衡器的情况完全相同,配置如下,这对我很有效。唯一的问题是客户端 IP 在 request.getRemoteAddr() 而不是 "X-Forwarded-For" 标头中

    frontend www
      bind *:80
      bind *:443 ssl crt crt_path
      redirect scheme https if !{ ssl_fc }
      mode http
      default_backend servers
    
    backend servers
      mode http
      balance roundrobin
      option forwardfor
      server S1 host1:port1 check
      server S2 host2:port2 check
      http-request set-header X-Forwarded-Port %[dst_port]
      http-request add-header X-Forwarded-Proto https if { ssl_fc }
    

    在 application.properties 中:

     server.use-forward-headers=true
    

    【讨论】:

      【解决方案6】:

      你试过设置吗

        server.context-path=/appname
      

      在 Spring Boot 中?

      【讨论】:

      • 较新版本:server.servlet.context-path
      【解决方案7】:

      尝试设置重写规则,如: https://proxyserver:4433/appname >>转发>> http://appserver:8080/appname

      然后将您的应用程序上下文设置为“appname” server.context-path=/appname

      因此,您可以在本地运行 http://appserver:8080/appname 并通过反向代理通过 https://proxyserver:4433/appname 访问

      由于我使用的是JBOSS,jboss的standalone.xm的变化:

      <http-listener name="default" socket-binding="http" redirect-socket="https" proxy-address-forwarding="true" enable-http2="true"/>
      

      Tomcat 会有类似的配置,以通知 Tomcat (proxy-address-forwarding="true") 尊重代理转发地址。

      【讨论】:

        【解决方案8】:

        server.use-forward-headers=true 对我不起作用,遇到了一个奇怪的问题,即 X-Forwarded-For 标头未始终填充到 HttpServletRequest

        最终使用 ForwardedHeaderFilter: https://stackoverflow.com/a/51500554/986942

        最重要的是,确保负载均衡器(代理)正确提供以下标头:

        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto https;
        

        【讨论】:

          猜你喜欢
          • 2022-01-19
          • 2021-03-03
          • 1970-01-01
          • 2019-11-27
          • 2017-01-15
          • 2018-05-28
          • 2015-07-25
          • 1970-01-01
          • 2015-07-21
          相关资源
          最近更新 更多