Выгрузка https для балансировщиков нагрузки с помощью Spring Security

#jsp #spring-mvc #https #spring-security #load-balancing

#jsp #spring-mvc #https #spring-безопасность #балансировка нагрузки

Вопрос:

Прямо сейчас балансировщики нагрузки обрабатывают https, а затем передают этот https на мои веб-серверы. Таким образом, обработка https удваивается для каждого запроса. То, что я хочу сделать, это полностью разгрузить https, чтобы моим веб-серверам не приходилось иметь с этим дело.

Как мне настроить страницы Spring Security и JSP, учитывая, что веб-серверы считают, что все запросы являются http? Очевидно, мне придется изменить <intercept-url> элементы моей конфигурации, чтобы их requires-channel атрибут всегда был http или any . На моих страницах JSP мне придется добавлять <c:url value=''/> ссылки с ${secureUrl} помощью и ${nonSecureUrl} в зависимости от того, должна ли результирующая страница быть https или http. Перенаправления с контроллеров также должны быть изменены подобным образом… Что-нибудь еще?

Кажется, довольно сложно изменить все ссылки на страницах JSP, чтобы включить схему и хост. Есть ли лучший способ сделать это?

Ответ №1:

Если вы завершаете работу SSL на балансировщике нагрузки, ваш балансировщик нагрузки должен отправить заголовок, указывающий, какой протокол был запрошен изначально. Например, F5 добавляет X-Forwarded-Proto .

Отсюда вы можете создавать пользовательские ChannelProcessor s, которые просматривают этот заголовок вместо просмотра request.isSecure() . Затем вы можете продолжить использовать <intercept-url requires-channel="https"> и relative <c:url> .

Шаги:

  1. Переопределение подклассов SecureChannelProcessor и InsecureChannelProcessor decide() . decide() Проверьте заголовок, отправленный вашим балансировщиком нагрузки.

     @Override
    public void decide(FilterInvocation invocation, Collection<ConfigAttribute> config) throws IOException, ServletException {
    
      for (ConfigAttribute attribute : config) {
          if (supports(attribute)) {
              if (invocation.getHttpRequest().
                      getHeader("X-Forwarded-Proto").equals("http")) {
                  entryPoint.commence(invocation.getRequest(),
                      invocation.getResponse());
              }
          }
      }
    }
      
  2. Затем установите эти ChannelProcessors в компоненте ChannelDecisionManagerImpl с помощью a BeanPostProcessor . См. Этот Часто задаваемые вопросы по безопасности Spring о том, почему / как использовать a BeanPostProcessor для этого.

Комментарии:

1. попробовал вышеуказанные шаги, метод decide не вызывается, есть ли что-нибудь еще, что нам нужно настроить?

Ответ №2:

Чтобы завершить отличный ответ на sourcedelica, вот полный код :

Для шага 1 :

 @Component
public class SecureChannelProcessorHack extends SecureChannelProcessor {

@Override
public void decide(FilterInvocation invocation, Collection<ConfigAttribute> config) throws IOException, ServletException {
    for (ConfigAttribute attribute : config) {
        if (supports(attribute)) {
            if ("http".equals(invocation.getHttpRequest().getHeader("X-Forwarded-Proto"))) {
                getEntryPoint().commence(invocation.getRequest(),
                        invocation.getResponse());
            }
        }
    }
}
}



@Component
public class InsecureChannelProcessorHack extends InsecureChannelProcessor {

@Override
public void decide(FilterInvocation invocation, Collection<ConfigAttribute> config) throws IOException, ServletException {
    for (ConfigAttribute attribute : config) {
        if (supports(attribute)) {
            if ("https".equals(invocation.getHttpRequest().getHeader("X-Forwarded-Proto"))) {
                getEntryPoint().commence(invocation.getRequest(),
                        invocation.getResponse());
            }
        }
    }
}
}
  

И шаг 2 :

 @Configuration
public class LoadBalancerHack implements BeanPostProcessor {

@Inject
SecureChannelProcessorHack secureChannelProcessorHack;

@Inject
InsecureChannelProcessorHack insecureChannelProcessorHack;

@Value("${behind.loadbalancer?false}")
boolean behindLoadBalancer;

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    return bean;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    if (behindLoadBalancer amp;amp; bean instanceof ChannelDecisionManagerImpl) {
        System.out.println("********* Post-processing "   beanName);
        ((ChannelDecisionManagerImpl) bean).setChannelProcessors(newArrayList(
                insecureChannelProcessorHack,
                secureChannelProcessorHack
        ));
    }
    return bean;
}

}
  

Ответ №3:

Похоже, Grails поддерживает это как часть плагина безопасности. Проверьте нижнюю часть http://grails-plugins.github.com/grails-spring-security-core/docs/manual/guide/17 Channel Security.html где они говорят о проверке заголовков запросов, которые установит LB.

 grails.plugins.springsecurity.secureChannel.useHeaderCheckChannelSecurity = true
grails.plugins.springsecurity.secureChannel.secureHeaderName = '...'
grails.plugins.springsecurity.secureChannel.secureHeaderValue = '...'
grails.plugins.springsecurity.secureChannel.insecureHeaderName = '...'
grails.plugins.springsecurity.secureChannel.insecureHeaderValue = '...'