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

Configure Application using Spring Security to accept HTTP requests and send HTTPS response

我是 Spring 框架的新手,我搜索了与该主题相关的所有可能的 SO link,但可以找到任何合适的解决方案。 我在 JBoss Wildfly 上有一个应用程序 运行。它位于 AWS EC2 实例上,该实例位于弹性负载均衡器 (ELB) 后面。 ELB 在其中配置了 SSL,因此它只接受来自客户端的 HTTPS 请求,但是我的应用程序服务器仅通过 HTTP 与 ELB 通信。我的应用程序使用 Spring MVC 和 Spring 安全性来进行 front-end/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>

此设置的问题是,一旦对应用程序进行身份验证 returns 到客户端的 HTTP link,客户端无法访问,因为 ELB 仅允许 HTTPS urls。如下所示:

客户端 --HTTPS---> ELB ---HTTP--> AppServer/Application

客户端 <--HTTP-- ELB <---HTTP--- AppServer/Application

但它实际上应该这样做,

客户端 --HTTPS---> ELB ---HTTP--> AppServer/Application

客户端 <--HTTPS-- ELB <---HTTPS--- AppServer/Application

正确的方法是什么,我是否需要使用任何类型的过滤器来绑定到 authentication/processing 之后的响应,在那里我可以将响应 url 更改为 HTTPS?

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

Spring 将通过以下方法调用检测到这一点:HttpServletRequest.isSecure(),这将始终 return 错误,因为从 ELB 到应用程序服务器的连接是通过 HTTP 而不是 HTTPS .

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

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

步骤简单:

  1. 创建自定义 HttpServletRequestWrapper

    public class CustomWrapper extends HttpServletRequestWrapper
    {
    
        public CustomWrapper(HttpServletRequest request) {
            super(request);
    
        }
    
       public boolean isSecure()
       {
            return true;
       }
    
        public String getScheme()
       {
           return "https";
       }
    }
    
  2. 创建将使用此包装器的过滤器

    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 {
    
       }  
    

    }

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