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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 00:01:47  来源:igfitidea点击:

Spring Boot with embedded Tomcat behind Apache proxy

springtomcatspring-securityreverse-proxyspring-boot

提问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