在Apache代理后面嵌入Tomcat的Spring Boot

时间:2014-08-22 21:45:01

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

我们有一个Spring Boot(Spring MVC)应用程序,在Apache SSL代理后面的专用appserver上嵌入了Tomcat。

代理服务器上的SSL端口为4433,转发到应用服务器上的端口8080。

因此代理服务器的URL转发如下:

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

当运行WITHOUT代理时,首先发生的事情是 Spring Security重定向请求,如:

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

通过

扩展WebSecurityConfigurerAdapter来显示登录表单
  ...
  httpSecurity.formLogin().loginPage("/login") ...
  ...

没有代理就可以正常工作,但是WITH代理需要更改重定向,
所以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

我们需要做些什么来使这个场景有效吗?


更新

另外,我担心" / appname"部分在代理URL中。在appserver上,应用程序的根源是" /"。 Spring应该如何指示" / appname"应该包含在通过代理发送回客户端的所有URL中吗?

8 个答案:

答案 0 :(得分:23)

I had the same problem the other day. After some debugging of Spring Boot 1.3 I found the following solution.

1. You have to setup the headers on your Apache proxy:

<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. You have to tell your Spring Boot app to use these headers. So put the following line in your application.properties (or any other place where Spring Boots understands properties):

server.use-forward-headers=true

If you do these two things correctly, every redirect your application sends will not go to http://127.0.0.1:8080/[path] but automatically to https://www.myapp.com/[path]

Update 1. The documentation about this topic is here. You should read it at least to be aware of the property server.tomcat.internal-proxies which defines the range of IP-addresses for proxy servers that can be trusted.

答案 1 :(得分:4)

您的代理看起来很好,后端应用也是如此,但它似乎没有看到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

或在其中设置断点。

答案 2 :(得分:2)

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

回应评论:

remote_ip_header和protocol_header弹簧启动配置值是什么?

让我们暂时忘记Spring Boot。 Tomcat是嵌入式servlet容器,它具有一个名为RemoteIpValve的阀门。该阀门是Apache remotip_module的一个端口。该阀门的主要目的是将发起请求的&#34;使用者视为原始使用者&#34;为了#34;授权和记录的目的&#34;。为了使用这种阀门,需要对其进行配置。

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

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

答案 3 :(得分:1)

我使用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

答案 4 :(得分:0)

您是否尝试过设置

  server.context-path=/appname

在Spring Boot中?

答案 5 :(得分:0)

尝试像这样设置重写规则: 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”)遵守代理转发地址。

答案 6 :(得分:0)

有几个与此相关的属性可以配置。 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

答案 7 :(得分:0)

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

最终使用了 ForwardedHeaderFilterhttps://stackoverflow.com/a/51500554/986942

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

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