当前位置: 首页 > 知识库问答 >
问题:

在负载平衡器后面使用Spring Security的Spring Boot:将HTTP重定向到HTTPS

鲍永春
2023-03-14

我正在使用Elastic Beanstalk部署Spring Boot web服务器,运行在应用程序负载平衡器(Elastic load balancer)后面。业务规则是,只能通过HTTPS联系此web服务器。因此,任何HTTP请求都必须首先转发到HTTPS。

基于这篇来自Amazon的文章,我应该简单地检查由负载平衡器设置的x-forwarded-forx-forwarded-proto头文件。这些标头包含有关客户端向负载平衡器发出的原始请求的信息。

所以我开始寻找Spring Boot的内置方法(我使用内置的Tomcat服务器,顺便说一句)来检查这两个头并进行重定向,而不必自己编写太多代码。Spring Boot(我们的版本是1.4)记录了使用以下应用程序属性的状态:

security.require_ssl=true
server.tomcat.remote_ip_header=x-forwarded-for
server.tomcat.protocol_header=x-forwarded-proto

用Postman测试一下,我得到了一个HTTP 200,在那里我应该得到一个301/302重定向。我怀疑这是因为我使用了Spring Security。为了让它与Spring Security一起工作,我可以将其添加到我的WebSecurity配置适配器中:

 http.requiresChannel().anyRequest().requiresSecure();

但现在我的头被忽略,所有请求都被转发到HTTPS,即使原始请求已经是HTTPS。所以现在什么都没用了。

罗德里戈·克萨达的回答似乎是我所需要的,但出于某种原因,它仍然不尊重我的头球。这个请求仍然会给我一个重定向,而不是200:

GET / HTTP/1.1
Host: localhost:8080
X-Forwarded-Proto: https
X-Forwarded-For: 192.168.0.30

这里和其他网站上还有许多其他解决方案,使用另一个中间web服务器,或者在AWS上配置NGINX或Apache来执行重定向。但我已经在使用Tomcat了,为什么还要配置另一个web服务器呢。

那么,我如何在Spring方式中配置Spring Boot/Tomcat/Spring Security来执行此重定向呢?是否有方法扩展要求安全()函数的行为,以便它考虑到我的请求头?或者更好的是,是否有任何Spring Security替代我尝试过的应用程序属性?


共有1个答案

夔光霁
2023-03-14

我现在用一点自定义逻辑修复了它。在将HttpServletRequest传递给任何REST控制器之前,可以注册一个HandlerInterceptor来检查它。它看起来像这样:

private HandlerInterceptor protocolInterceptor() {
    return new HandlerInterceptorAdapter() {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
            String forwardedProtocol = request.getHeader("x-forwarded-proto");

            // x-forwarded-proto header is required, send 400 if it's missing
            if (forwardedProtocol == null) {
                response.sendError(HttpServletResponse.SC_BAD_REQUEST, "x-forwarded-proto header required from the load balancer");
                return false;
            }

            // Client request protocol must be https, send 301 if it's http
            if (!forwardedProtocol.equals("https")) {
                String host = request.getHeader("host");

                // Send error 400 if 'host' is empty
                if (host == null) {
                    response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Host header missing");
                    return false;
                }

                response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
                response.setHeader("Location", "https://" + host + request.getServletPath());
                return false;
            }

            // All is well in all other cases, continue
            return true;
        }
    };
}

我首先检查是否存在x-转发-原头。如果不存在,我会发送一个客户端错误响应。然后我检查头值,它必须是https。HTTP 1.1总是有一个host头,但以防万一我也检查了空值。最后,我发送了一个301永久重定向。请注意,我把我的拦截器放在了一个方法中,但你也可以用它做一个bean。

接下来,我们需要注册拦截器。如果您还没有,请创建一个扩展WebMvcConfigrerAdapter配置类,并覆盖addInterceptors方法:

@Override
public void addInterceptors(InterceptorRegistry registry) {
    logger.info("Registering custom interceptors");
    logger.debug("Registering protocol interceptor to redirect http to https");
    registry.addInterceptor(protocolInterceptor());
    logger.info("Registered custom interceptors");
}

在开发/测试期间禁用此配置类,或者确保在来自测试库(如MockMVC)的所有请求中设置适当的头,否则您将破坏这些测试。

如果我有空余时间,我会努力让现成的解决方案发挥作用,但现在,这将(不得不)做到。

 类似资料:
  • 以下是我的设置: EC2与Apache使用弹性负载均衡器。 我希望所有超文本传输协议流量都自动重定向到https。我找到了这个reco,并通过添加到我的httpd.conf文件中来尝试: 然而,这在我重新启动服务器之前或之后都不起作用。HTTP没有重定向,我的站点抛出了各种错误,直到我从配置中删除了该规则。 我认为我更新文件错误或负载均衡器设置不正确。对于负载均衡器的侦听器,我有LB协议HTTP,

  • 问题是,我还想将HTTP访问重定向到HTTPS,然后我试图添加一个规则将80重定向到443,因此它将落入第一个规则(443到8080)并使用证书,但它不起作用。在网上研究,我发现我应该在我的。htacess文件中添加一些行,但也不起作用。我认为这不是我的解决方案,因为所有HTTPS的东西都在AWS端,有没有一种方法可以只通过AWS将HTTP重定向到HTTPS而不改变服务器?

  • 我有一个非常简单的Spring Boot应用程序,具有社交单点登录功能。 看起来是这样的: 它在应用程序中有必需的条目。yml: 它在我的本地机器上运行得很好,并且只启动了一个实例。 当有多个实例隐藏在负载平衡器后面时,就会出现问题。 即使用户在第一个请求中进行身份验证,向负载均衡器发出的后续请求也会因401而被阻止。 与第一个应用程序实例相比,请求被路由到不同的应用程序实例。 我正在试图弄清楚,

  • web-service预期会有很多调用,而我希望在出现故障时使该服务成为冗余,因此我希望有两个实例同时运行以处理所有请求。 1)让两个级别的Web服务同时处理请求的最佳方法是什么?使用外部负载均衡器还是使用AKKA/AKKA-HTTP中的某种魔法(我不知道)? 2)我必须调整哪些主要参数来提高性能?

  • 我希望在现有的服务器设置中集成WAF,因为我有经典的负载平衡器(带有EC2实例),它不支持WAF,我需要迁移到应用程序负载平衡器。 是否可以在不更改DNS(记录)的情况下将现有的经典负载平衡器迁移到应用程序负载平衡器?

  • 我遵循了GKE教程,使用beta Inrit类型创建HTTP负载均衡器,并且在使用nginx映像时工作正常。我的问题是为什么Inrit是必要的。 我可以创建一个容器引擎集群,然后创建一个使用库伯内特斯创建的实例组作为服务后端的HTTP负载均衡器,并且一切似乎都运行良好。当仅在部分流程中使用库伯内特斯似乎运行良好时,为什么我要经历使用Inete的所有麻烦?