java 在反向代理后面需要带有 Spring Security 的 HTTPS
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/30076551/
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
Require HTTPS with Spring Security behind a reverse proxy
提问by Serge Ballesta
I have a Spring MVC application secured with Spring Security. The majority of the application uses simple HTTP to save resources, but a small part processes more confidential information and requires an HTTPS channel.
我有一个使用 Spring Security 保护的 Spring MVC 应用程序。大多数应用程序使用简单的 HTTP 来节省资源,但一小部分处理更机密的信息并需要 HTTPS 通道。
Extract from the security-config.xml
:
摘自security-config.xml
:
<sec:http authentication-manager-ref="authenticationManager" ... >
...
<sec:intercept-url pattern="/sec/**" requires-channel="https"/>
<sec:intercept-url pattern="/**" requires-channel="http"/>
</sec:http>
All worked fine until we decided to migrate it to the main server, where the application servers run behind reverse proxies. And as now HTTPS is processed by the reverse proxies the application server only sees HTTP requests, and disallows access to the /sec/**
hierarchy.
一切正常,直到我们决定将其迁移到主服务器,应用服务器在反向代理之后运行。现在 HTTPS 由反向代理处理,应用程序服务器只能看到 HTTP 请求,并且不允许访问/sec/**
层次结构。
After some research, I found that the proxies add a X-Forwarded-Proto: https
header (*), but in Spring Security HttpServletRequest.isSecure()
is used to determine the channel security offered(extract from SecureChannelProcessor
javadoc).
经过一番研究,我发现代理添加了一个X-Forwarded-Proto: https
标头(*),但在 Spring SecurityHttpServletRequest.isSecure()
中用于确定提供的通道安全性(摘自SecureChannelProcessor
javadoc)。
How can I tell Spring Security that a X-Forwarded-Proto: https
header is enough for a secure request?
我如何告诉 Spring Security 一个X-Forwarded-Proto: https
标头对于安全请求来说已经足够了?
I know I could report that part on proxies configuration, but the proxies administrator really does not like that solution, because there are many application behind the proxies and the configuration could grow to a non manageable state.
我知道我可以报告关于代理配置的那部分,但是代理管理员真的不喜欢那个解决方案,因为代理背后有许多应用程序,并且配置可能会增长到不可管理的状态。
I an currently using Spring Security 3.2 with XML config, but I'm ready to accept answers based on Java config and/or more recent version.
我目前使用带有 XML 配置的 Spring Security 3.2,但我准备接受基于 Java 配置和/或更新版本的答案。
(*)Of course, the proxies remove the header if it was present in incoming request, so the application can be confident in it.
(*)当然,如果传入请求中存在标头,代理会删除标头,因此应用程序可以对其有信心。
采纳答案by Neil McGuigan
If your site is HTTPS and you're running Apache Tomcat behind another system that's handling TLS termination, you can tell Tomcat to "pretend" that it's handling the TLS termination.
如果您的站点是 HTTPS 并且您在另一个处理 TLS 终止的系统后面运行 Apache Tomcat,您可以告诉 Tomcat“假装”它正在处理 TLS 终止。
This makes request.isSecure()
return true
;
这使得request.isSecure()
return true
;
To do so, you need to add secure="true"
to your Connector config in server.xml
.
为此,您需要secure="true"
在server.xml
.
https://tomcat.apache.org/tomcat-7.0-doc/config/http.html
https://tomcat.apache.org/tomcat-7.0-doc/config/http.html
See also the scheme
attribute.
另请参见scheme
属性。
回答by Serge Ballesta
Kind of a followup to NeilMcGuigan's answer that showed that the solution was servlet container side.
NeilMcGuigan 的回答的一种跟进,表明解决方案是 servlet 容器端。
Tomcat is even better. There is a valvededicated to maskingthe side effects of a reverse proxy. Extract from Tomcat documentation for Remote IP Valve:
Tomcat 甚至更好。有一个阀门专门用于掩盖反向代理的副作用。摘自远程 IP Valve 的Tomcat 文档:
Another feature of this valve is to replace the apparent scheme (http/https), server port and request.secure with the scheme presented by a proxy or a load balancer via a request header (e.g. "X-Forwarded-Proto").
此阀的另一个功能是用代理或负载均衡器通过请求标头(例如“X-Forwarded-Proto”)呈现的方案替换明显的方案(http/https)、服务器端口和 request.secure。
Example of the valve configuration :
阀门配置示例:
<Valve className="org.apache.catalina.valves.RemoteIpValve"
internalProxies="192\.168\.0\.10|192\.168\.0\.11"
remoteIpHeader="x-forwarded-for" proxiesHeader="x-forwarded-by"
protocolHeader="x-forwarded-proto" />
That way with no other configuration of the application itself, the call to Request.isSecure()
will return true if the request contains a header field of X-Forwarded-Proto=https
.
这样,在没有应用程序本身的其他配置的情况下,Request.isSecure()
如果请求包含X-Forwarded-Proto=https
.
I had thought of two other possibilities, but definitively prefere that one :
我曾想过另外两种可能性,但绝对喜欢那种可能性:
- use a filter active before Spring Security
ChannelProcessingFilter
to wrap the request with aHttpServletRequestWrapper
overridingisSecure()
to process aX-Forwarded-Proto
header - need writing and testing the filter and the wrapper - use a Spring
BeanPostProcessor
to look for aChannelProcessingFilter
and manually inject aChannelDecisionManager
able to consider theX-Forwarded-Proto
header - really too low level
- 使用过滤器活跃的Spring Security之前
ChannelProcessingFilter
包裹的请求,以此HttpServletRequestWrapper
覆盖isSecure()
到处理X-Forwarded-Proto
头-需要编写和测试过滤器和包装 - 使用 Spring
BeanPostProcessor
寻找一个ChannelProcessingFilter
并手动注入一个ChannelDecisionManager
能够考虑X-Forwarded-Proto
标题的 - 真的太低级了
回答by Roman Nikitchenko
Spring Boot makes it dead simple (at least with embedded Tomcat).
Spring Boot 让它变得非常简单(至少对于嵌入式 Tomcat)。
1. Add the following lines to your application.properties:
1. 将以下行添加到您的 application.properties:
server.forward-headers-strategy=native
server.tomcat.remote-ip-header=x-forwarded-for
server.tomcat.protocol-header=x-forwarded-proto
2. Do the following trick with your HttpSecurity
configuration.
2. 使用您的HttpSecurity
配置执行以下技巧。
// final HttpSecurity http = ...
// Probably it will be in your `WebSecurityConfigurerAdapter.configure()`
http.requiresChannel()
.anyRequest().requiresSecure()
Source is Spring Boot reference guide
来源是 Spring Boot 参考指南
84.3 Enable HTTPS When Running behind a Proxy Server
Please also check the answer below for a specifics related to Spring Boot 2.2
另请查看下面的答案以了解与 Spring Boot 2.2 相关的细节