Apache 代理后面带有嵌入式 Tomcat 的 Spring Boot
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25455969/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Spring Boot with embedded Tomcat behind Apache proxy
提问by Rop
We have a Spring Boot (Spring MVC) app with embedded Tomcat on a dedicated appserver behind an Apache SSL proxy.
我们有一个 Spring Boot (Spring MVC) 应用程序,在 Apache SSL 代理后面的专用应用程序服务器上嵌入了 Tomcat。
The SSL port on the proxy server is 4433, forwarding to port 8080 on the appserver.
代理服务器上的 SSL 端口是 4433,转发到应用服务器上的端口 8080。
So the URL to the proxy server is forwarding like:
因此,代理服务器的 URL 转发如下:
https://proxyserver:4433/appname >>forward>> http://appserver:8080/
When running WITHOUT proxy, the first thing that happens is that
Spring Security redirects the request, like:
在没有代理的情况下运行时,发生的第一件事是
Spring Security 重定向请求,例如:
http://appserver:8080/ >>redirect>> http://appserver:8080/login
to display the login form, by extending WebSecurityConfigurerAdapterwith
以显示登录表单,通过扩展WebSecurityConfigurerAdapter与
...
httpSecurity.formLogin().loginPage("/login") ...
...
It works fine without the proxy, but WITH proxy the redirect needs to be changed,
so Spring should instead redirect to the corresponding proxy URL, like:
它在没有代理的情况下工作正常,但是 WITH proxy 需要更改重定向,
因此 Spring 应该重定向到相应的代理 URL,例如:
http://appserver:8080/ >>redirect>> https://proxyserver:4433/appname/login
but no success yet.
但还没有成功。
I am trying to apply this solution: 59.8 Use Tomcat behind a front-end proxy server
我正在尝试应用此解决方案: 59.8 Use Tomcat behind a front-end proxy server
We have configured mod_proxyin Apache, and verified that it sends the expected headers:
我们已经在 Apache 中配置了mod_proxy,并验证它发送了预期的标头:
X-Forwarded-For: xxx.xxx.xxx.xxx
X-Forwarded-Host: proxyserver
X-Forwarded-Port: 4433
X-Forwarded-Proto: https
The application is started with parameters:
应用程序以参数启动:
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
Still the redirect does not work.
仍然重定向不起作用。
It will keep redirecting locally, to http://appserver:8080/loginwhich is not available to the clients.
它将继续在本地重定向,http://appserver:8080/login客户端无法访问该重定向 。
Is there anything else we need to do to make this scenario work?
我们还需要做些什么来使这个场景起作用?
UPDATE
更新
Also, I am concerned about the "/appname" part in the proxy URL. On the appserver the application is rooted at "/". How should Spring be instructed that "/appname" should be included in all URLs sent back to the clients, when going thru the proxy?
另外,我担心代理 URL 中的“/appname”部分。在应用程序服务器上,应用程序以“/”为根。在通过代理时,应该如何指示 Spring 将“/appname”包含在发送回客户端的所有 URL 中?
采纳答案by Igor Mukhin
I had the same problem the other day. After some debugging of Spring Boot 1.3 I found the following solution.
前几天我遇到了同样的问题。在对 Spring Boot 1.3 进行一些调试后,我找到了以下解决方案。
1.You have to setup the headers on your Apache proxy:
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.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):
2.您必须告诉您的 Spring Boot 应用程序使用这些标头。所以把下面这行放在你的 application.properties (或任何其他 Spring Boots 理解属性的地方):
server.use-forward-headers=true
If you do these two things correctly, every redirect your application sends will notgo to http://127.0.0.1:8080/[path]but automatically to https://www.myapp.com/[path]
如果你正确地做这两件事,你的应用程序发送的每个重定向都不会转到http://127.0.0.1:8080/[path]而是自动转到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-proxieswhich defines the range of IP-addresses for proxy servers that can be trusted.
更新 1.关于这个主题的文档在这里。您至少应该阅读它以了解server.tomcat.internal-proxies定义可以信任的代理服务器的 IP 地址范围的属性。
回答by Dave Syer
Your proxy looks fine, and so does the backend app, up to a point, but it doesn't seem to be seeing the RemoteIpValvemodified request. The default behaviour of the RemoteIpValveincludes a pattern match for the proxy IP address (as a security check) and it only modifies requests that it thinks are from a valid proxy. The pattern defaults in Spring Boot to a well-known set of internal IP addresses like 10.*.*.*and 192.168.*.*, so if your proxy isn't on one of those you need to explicitly configure it, e.g.
您的代理看起来不错,后端应用程序也是如此,在某种程度上,但它似乎没有看到RemoteIpValve修改后的请求。的默认行为RemoteIpValve包括代理 IP 地址的模式匹配(作为安全检查),它只修改它认为来自有效代理的请求。Spring Boot 中的模式默认为一组众所周知的内部 IP 地址,例如10.*.*.*and 192.168.*.*,因此如果您的代理不在其中之一,则您需要显式配置它,例如
server.tomcat.internal-proxies=172\.17\.\d{1,3}\.\d{1,3}|127\.\d{1,3}\.\d{1,3}\.\d{1,3}
(using properties file format, which means you have to double escape the backslashes).
(使用属性文件格式,这意味着您必须对反斜杠进行双重转义)。
You can see the what is happening in the RemoteIpValveif you set
RemoteIpValve如果你设置,你可以看到发生了什么
logging.level.org.apache.catalina.valves.RemoteIpValve=DEBUG
or set a breakpoint in it.
或在其中设置断点。
回答by Newbie
A typical solution to this problem is to let the proxy handle any required rewrite. For example, in Apache you can use the rewrite_moduleand/or headers_moduleto correct headers. As another example, Nginx handles this and other similar cases automatically for you after configuring upstream servers.
这个问题的典型解决方案是让代理处理任何需要的重写。例如,在 Apache 中,您可以使用rewrite_module和/或headers_module来更正标头。再举一个例子,Nginx 会在配置上游服务器后自动为你处理这种情况和其他类似的情况。
In response to comments:
回应评论:
What are the remote_ip_header and protocol_header spring boot configuration values?
remote_ip_header 和 protocol_header spring boot 配置值是什么?
Let's forget Spring Boot for a moment. Tomcat, the embedded servlet container, features a valve known as the RemoteIpValve. This valve is a port of the Apache remotip_module. The primary purpose of this valve is to treat the "useragent which initiated the request as the originating useragent" for "the purposes of authorization and logging". In order for this valve to be used it needs to be configured.
让我们暂时忘记 Spring Boot。Tomcat 是嵌入式 servlet 容器,具有称为 RemoteIpValve 的阀门。这个阀是 Apache remotip_module 的一个端口。此阀的主要目的是将“发起请求的用户代理视为原始用户代理”以实现“授权和记录的目的”。为了使用该阀,需要对其进行配置。
Please find more information about this valve here.
请在此处找到有关此阀门的更多信息。
Spring Boot conveniently supports configuring this valve via application.properties through the server.tomcat.remote_ip_header and server.tomcat.protocol_header properties.
Spring Boot 通过 server.tomcat.remote_ip_header 和 server.tomcat.protocol_header 属性通过 application.properties 方便地支持配置此阀。
回答by Panagiotis Theocharis
I had exactly the same case using haproxy as load balancer with the below configuration, which worled for me. The only thing is the client IP is in request.getRemoteAddr()and not in "X-Forwarded-For"header
我有完全相同的情况,使用 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 }
In application.properties:
在 application.properties 中:
server.use-forward-headers=true
回答by Piotr Gwiazda
Have you tried setting
你有没有试过设置
server.context-path=/appname
In Spring Boot?
在 Spring Boot 中?
回答by Sagar Mody
Try setting the Rewrite rule like: https://proxyserver:4433/appname>>forward>> http://appserver:8080/appname
尝试设置重写规则,如: https://proxyserver:4433/appname>>forward>> http://appserver:8080/appname
And then set your application context to "appname" server.context-path=/appname
然后将您的应用程序上下文设置为“appname” server.context-path=/appname
So locally you can run by http://appserver:8080/appnameand via Reverse Proxy you access via https://proxyserver:4433/appname
因此,您可以在本地通过http://appserver:8080/appname 运行并通过反向代理通过https://proxyserver:4433/appname 访问
Since I am using JBOSS, changes in standalone.xm of jboss:
由于我使用的是 JBOSS,因此在 jboss 的 standalone.xm 中发生了变化:
<http-listener name="default" socket-binding="http" redirect-socket="https" proxy-address-forwarding="true" enable-http2="true"/>
Tomcat would have similar config, to inform Tomcat (proxy-address-forwarding="true") to respect the proxy forwarding address.
Tomcat 会有类似的配置,通知 Tomcat (proxy-address-forwarding="true") 尊重代理转发地址。
回答by Mark Lagendijk
There are several properties that you can configure, related to this.
application.yamlexample:
您可以配置几个与此相关的属性。
application.yaml例子:
server:
forward-headers-strategy: native
tomcat:
use-relative-redirects: true
protocol-header: x-forwarded-proto
remote-ip-header: x-forwarded-for
Setting server.forward-headers-strategy: nativeis the replacement of the deprecated server.use-forward-headers:true
设置server.forward-headers-strategy: native是对已弃用的替换server.use-forward-headers:true

