Spring Boot с Spring Security за балансировщиком нагрузки: перенаправить HTTP на HTTPS - PullRequest
0 голосов
/ 03 июля 2018

Я использую Elastic Beanstalk для развертывания веб-сервера Spring Boot, работающего за балансировщиком нагрузки приложения (Elastic Load Balancer). Бизнес-правило заключается в том, что с этим веб-сервером следует связываться только через HTTPS. Поэтому любой HTTP-запрос сначала должен быть перенаправлен на HTTPS.

Основываясь на этой статье от Amazon, я должен просто проверить заголовки x-forwarded-for и x-forwarded-proto, которые устанавливаются балансировщиком нагрузки. Эти заголовки содержат информацию об исходном запросе, выполненном клиентом для балансировщика нагрузки.

Поэтому я начал искать встроенные способы для Spring Boot (я использую встроенный Tomcat-сервер, кстати) для проверки этих двух заголовков и перенаправления без необходимости писать слишком много кода самостоятельно. Spring Boot (мы находимся на версии 1.4) docs заявляет, чтобы использовать следующие свойства приложения:

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 . Так что теперь ничего не работает.

Ответ Родриго Кесады здесь , кажется, то, что мне нужно, но он по-прежнему не уважает мои заголовки, по некоторым причинам. Этот запрос все еще дает мне перенаправление вместо 200:

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

Существует множество других решений здесь и на других веб-сайтах, использующих другой промежуточный веб-сервер или настраивающих NGINX или Apache на AWS для выполнения перенаправления. Но я уже использую Tomcat, зачем мне настраивать еще один веб-сервер.

Так как я могу на самом деле настроить Spring Boot / Tomcat / Spring Security в Spring-way, чтобы сделать это перенаправление? Есть ли способ расширить поведение функции requiresSecure(), чтобы она учитывала заголовки моих запросов? Или, что еще лучше, есть ли у Spring Security альтернативы свойствам приложений, которые я пробовал?

1 Ответ

0 голосов
/ 05 июля 2018

Я исправил это сейчас с помощью некоторой пользовательской логики. 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, иначе вы нарушите эти тесты.

Если у меня будет свободное время, я постараюсь заставить работать готовое решение, но сейчас это будет (придется) делать.

...