使用 Spring Security 配置应用程序以接受 HTTP 请求并发送 HTTPS 响应

Posted

技术标签:

【中文标题】使用 Spring Security 配置应用程序以接受 HTTP 请求并发送 HTTPS 响应【英文标题】:Configure Application using Spring Security to accept HTTP requests and send HTTPS response 【发布时间】:2015-06-10 01:26:15 【问题描述】:

我是 Spring 框架的新手,我已经搜索了与该主题相关的所有可能的 SO 链接,但可以找到任何合适的解决方案。 我有一个在 JBoss Wildfly 上运行的应用程序。它位于 AWS EC2 实例上,该实例位于 Elastic Load Balancer (ELB) 后面。 ELB 中配置了 SSL,因此它只接受来自客户端的 HTTPS 请求,但是我的应用程序服务器仅通过 HTTP 与 ELB 通信。我的应用程序同时使用 Spring MVC 和 Spring Security 进行前端/安全管理。 我的 security-context.xml 文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">

<context:property-placeholder location="/WEB-INF/config.properties" />
<context:annotation-config></context:annotation-config>
<context:component-scan base-package="com.etech.security">          </context:component-scan>
<security:authentication-manager>
    <security:authentication-provider>
        <security:user-service>
            <security:user name="xxx@xxxx.com"
                authorities="admin" password="xxxxxxxx" />
        </security:user-service>
</security:authentication-provider>
</security:authentication-manager>
<security:http use-expressions="true">
    <security:intercept-url pattern="/" access="isAuthenticated()" />
    <security:intercept-url pattern="/dashboard"
        access="isAuthenticated()" />
    <security:intercept-url pattern="/resources/**" access="permitAll" />
    <security:intercept-url pattern="/login" access="permitAll" />
    <security:intercept-url pattern="/**" access="denyAll" />
    <security:form-login login-page="/login"
        authentication-success-handler-ref="asyncAuthenticationSuccessHandler"
        authentication-failure-handler-ref="asyncAuthenticationFailureHandler" />
    <security:logout logout-url="/logout" logout-success-url="/" />

</security:http>
<bean id="asyncAuthenticationSuccessHandler"
    class="com.etech.security.AsyncAuthenticationSuccessHandler">
<constructor-arg ref="supportMailPassword"></constructor-arg>
</bean>

此设置的问题是,一旦通过身份验证,应用程序就会向客户端返回一个 HTTP 链接,客户端无法访问该链接,因为 ELB 只允许 HTTPS url。如下所示:

Client --HTTPS---> ELB ---HTTP--> AppServer/Application

客户端

但它实际上应该这样做,

Client --HTTPS---> ELB ---HTTP--> AppServer/Application

客户端

什么是正确的方法,我是否需要使用任何类型的过滤器在身份验证/处理后绑定到响应,我可以将响应 URL 更改为 HTTPS?

【问题讨论】:

你试过这个解决方案***.com/questions/8002272/…吗? 我尝试了该解决方案,但由于某种原因,InsecureChannelProcessor 无法从 ELB 检测到“X-Forwarded-Proto”标头,但 SecureChannelProcessor 可以。我绕过了 Insecure 中的标头检查以通过安全通道开始所有流量,但它仍在将 http url 发送回客户端。 【参考方案1】:

在深入研究问题后,我发现这主要是由于 Spring Dispatcher Servlet 检测到入站连接为不安全,即所有连接都通过 HTTP,这将导致所有出站连接也通过 HTTP .

Spring 将通过以下方法调用检测到这一点:HttpServletRequest.isSecure(),这将始终返回 false,因为从 ELB 到 Application Server 的连接是通过 HTTP 而不是 HTTPS。

有一个很好的blog post 描述了这个完全相同的问题,并发布了一个我自己使用的简单解决方案。

解决方案是使用放置在过滤器链顶部的过滤器。它会拦截所有传入的请求,用自定义的 HttpServletRequest 替换入站的 HttpServletRequest,这将使 isSecure() 方法返回 true。 Spring dispatcher Servlet 会检测到这一点并将 HTTPS 响应发送回客户端。

步骤很简单:

    创建自定义 HttpServletRequestWrapper

    public class CustomWrapper extends HttpServletRequestWrapper
    
    
        public CustomWrapper(HttpServletRequest request) 
            super(request);
    
        
    
       public boolean isSecure()
       
            return true;
       
    
        public String getScheme()
       
           return "https";
       
    
    

    创建一个过滤器来使用这个包装器

    public class CustomFilter implements Filter 
    
    
        @Override
        public void destroy() 
    
        
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException 
        
            CustomWrapper wrapper = new CustomWrapper((HttpServletRequest) request);
            chain.doFilter(wrapper, response);
    
        
    
        @Override
        public void init(FilterConfig arg0) throws ServletException 
    
         
    

    在 web.xml 中,将此过滤器添加到任何其他过滤器之前,包括 Spring Security 过滤器链。

【讨论】:

以上是关于使用 Spring Security 配置应用程序以接受 HTTP 请求并发送 HTTPS 响应的主要内容,如果未能解决你的问题,请参考以下文章

在运行时延迟初始化 Spring Security + 重新加载 Spring Security 配置

Spring:使用 Spring Security 为执行器端点配置安全性

Spring:使用Spring Security配置执行器端点的安全性

如何使用自定义 JAAS 堆栈配置 Spring Security?

Spring Security - 多种配置 - 添加 LogoutHandler

如何配置 Spring Security SAML 以与 Okta 一起使用?