[英]Spring Boot with embedded Tomcat behind Apache proxy
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 WebSecurityConfigurerAdapter
with通过扩展
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:所以 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_proxy in 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/login
which is not available to the clients.它将继续在本地重定向到客户端无法使用的
http://appserver:8080/login
。
Is there anything else we need to do to make this scenario work?我们还需要做些什么来使这个场景起作用?
Also, I am concerned about the "/appname" part in the proxy URL.另外,我担心代理 URL 中的“/appname”部分。 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?
当通过代理时,应该如何指示 Spring 将“/appname”包含在发送回客户端的所有 URL 中?
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. 2.您必须告诉您的 Spring Boot 应用程序使用这些标头。 So put the following line in your application.properties (or any other place where Spring Boots understands properties):
所以把下面这行放在你的 application.properties (或任何其他 Spring Boots 理解属性的地方):
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]如果你正确地做这两件事,你的应用程序发送的每个重定向都不会转到http://127.0.0.1:8080/[path]而是自动转到https://www.myapp.com/[path]
Update 1. The documentation about this topic is here .更新 1.关于这个主题的文档在这里。 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.您至少应该阅读它以了解属性
server.tomcat.internal-proxies
,它定义了可以信任的代理服务器的 IP 地址范围。
Update 2021 The documentation is moved to here .更新 2021文档移至此处。 The Spring Boot configuration is a litte different now.
Spring Boot 配置现在有点不同。
Your proxy looks fine, and so does the backend app, up to a point, but it doesn't seem to be seeing the RemoteIpValve
modified request.您的代理看起来不错,后端应用程序也是如此,在某种程度上,但它似乎没有看到
RemoteIpValve
修改后的请求。 The default behaviour of the RemoteIpValve
includes 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. RemoteIpValve
的默认行为包括代理 IP 地址的模式匹配(作为安全检查),它只修改它认为来自有效代理的请求。 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, eg 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}
(using properties file format, which means you have to double escape the backslashes). (使用属性文件格式,这意味着您必须对反斜杠进行双重转义)。
You can see the what is happening in the RemoteIpValve
if you set如果您设置,您可以看到
RemoteIpValve
发生的事情
logging.level.org.apache.catalina.valves.RemoteIpValve=DEBUG
or set a breakpoint in it.或在其中设置断点。
A typical solution to this problem is to let the proxy handle any required rewrite.这个问题的典型解决方案是让代理处理任何需要的重写。 For example, in Apache you can use the rewrite_module and/or headers_module to correct headers.
例如,在 Apache 中,您可以使用rewrite_module和/或headers_module来更正标头。 As another example, Nginx handles this and other similar cases automatically for you after configuring upstream servers.
再举一个例子,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.让我们暂时忘记 Spring Boot。 Tomcat, the embedded servlet container, features a valve known as the RemoteIpValve.
Tomcat 是嵌入式 servlet 容器,具有称为 RemoteIpValve 的阀门。 This valve is a port of the Apache remotip_module .
这个阀是 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.
为了使用该阀,需要对其进行配置。
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 方便地支持配置此阀。
I had exactly the same case using haproxy as load balancer with the below configuration, which worled for me.我有完全相同的情况,使用 haproxy 作为具有以下配置的负载均衡器,这对我来说很合适。 The only thing is the client IP is in
request.getRemoteAddr()
and not in "X-Forwarded-For"
header唯一的问题是客户端 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
There are several properties that you can configure, related to this.您可以配置几个与此相关的属性。
application.yaml
example: 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: native
is the replacement of the deprecated server.use-forward-headers:true
设置
server.forward-headers-strategy: native
是对已弃用的server.use-forward-headers:true
的替换
Have you tried setting你有没有试过设置
server.context-path=/appname
In Spring Boot?在 Spring Boot 中?
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/appname and 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") 尊重代理转发地址。
server.use-forward-headers=true
did not work for me, experienced a weird issue where X-Forwarded-For
header is not populated to HttpServletRequest
consistently. server.use-forward-headers=true
对我不起作用,遇到了一个奇怪的问题,即X-Forwarded-For
标头未一致地填充到HttpServletRequest
。
Ended up using ForwardedHeaderFilter
: https://stackoverflow.com/a/51500554/986942 .最终使用
ForwardedHeaderFilter
: https : //stackoverflow.com/a/51500554/986942 。
On top of that, make sure the load balancer (proxy) provide the following headers properly:最重要的是,确保负载均衡器(代理)正确提供以下标头:
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto https;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.