Spring Boot with Spring Security behind Load Balancer:将HTTP



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

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

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

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

用邮递员测试这个给了我一个 HTTP 200,我应该得到一个 301/302 重定向。我怀疑这是由于我使用了Spring Security。为了使它与 Spring Security 一起使用,我可以将其添加到我的WebSecurityConfigurerAdapter

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

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

罗德里戈·克萨达(Rodrigo Quesada(在这里的答案似乎是我需要的,但由于某种原因,它仍然不尊重我的标题。这个请求仍然给了我一个重定向而不是 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()函数的行为,以便它考虑到我的请求标头?或者更好的是,除了我尝试过的应用程序属性之外,是否有任何 Spring Security 替代方案?

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

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标头是否存在。如果没有,我会发送客户端错误响应。然后我检查标头值,它必须是https。HTTP 1.1 始终有一个host标头,但以防万一我也检查空值。最后我发送了 301 永久重定向。请注意,我把我的拦截器放在一个方法中,但你也可以用它做一个豆子。

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

如果我有一些空闲时间,我会尝试让开箱即用的解决方案工作,但现在,这将(必须(做到。

最新更新