java - 负载均衡器 : redirect HTTP to HTTPS 后面带有 Spring Security 的 Spring Boot

标签 java amazon-web-services spring-boot tomcat spring-security

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

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

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

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 一起工作,我可以将其添加到我的 WebSecurityConfigurerAdapter:

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

但现在我的 header 被忽略并且所有请求都被转发到 HTTPS,即使原始请求已经是 HTTPS。所以现在没有任何效果。

Rodrigo Quesada 的回答 here似乎是我需要的,但出于某种原因,它仍然不尊重我的标题。这个请求仍然给我重定向而不是 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 来执行此重定向?有没有办法扩展 requiresSecure() 函数的行为,以便它考虑我的请求 header ?或者更好的是,是否有任何 Spring Security 替代我尝试过的应用程序属性?

最佳答案

我现在用一些自定义逻辑修复了它。可以注册 HandlerInterceptor 以在 HttpServletRequest 传递给任何 REST Controller 之前对其进行检查。它看起来像这样:

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-forwarded-proto header 是否存在。如果没有,我会发送客户端错误响应。然后我检查 header 值,它必须是 https。 HTTP 1.1 总是有一个 host header ,但为了以防万一,我也检查它是否有空值。最后我发送了一个 301 永久重定向。请注意,我将拦截器放在一个方法中,但您也可以从中创建一个 bean。

接下来我们需要注册拦截器。如果您还没有,请创建一个扩展 WebMvcConfigurerAdapter 的配置类并覆盖 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)的所有请求中设置适当的 header ,否则您将破坏这些测试。

如果我有空闲时间,我会尝试让开箱即用的解决方案发挥作用,但就目前而言,这将(必须)做到。

关于java - 负载均衡器 : redirect HTTP to HTTPS 后面带有 Spring Security 的 Spring Boot,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51151621/

相关文章:

java - 使用 API(Java?)在 Facebook 粉丝页面上发布状态

Java : JFormattedTextField saving numbers

java - 使用CSRF登录后如何启用Spring Security POST重定向?

MySQL - 如何合并来自两个表的数据,使用一个字段的值作为过滤器来计算另一个字段中的值?

amazon-web-services - 如何使用 aws cloudformation 模板在 aws cognito 用户池中设置所需属性?

java - 如何使用 Java RabbitMQ 发送和接收文件?

mysql - Amazon EC2 - 无法远程连接到 MariaDB

spring - 仅当设置了特定 Spring 配置文件时才运行集成测试

spring - 带有List <X>的通用bean的Spring Boot(Kotlin)自动接线失败

java - Spring Boot 2.0.0,在自动配置 jar 中找不到 DataSourceBuilder