Поддержка Servlet 3 Async с Spring MVC и Spring Security

Поддержка Servlet 3 Async с Spring MVC и Spring Security

1. Вступление

В этом кратком руководстве мы сосредоточимся наthe Servlet 3 support for async requests, and how Spring MVC and Spring Security handle these.

Самая основная мотивация асинхронности в веб-приложениях - обработка длительных запросов. В большинстве случаев нам нужно убедиться, что принципал Spring Security распространяется на эти потоки.

И, конечно же, Spring Securityintegrates with @Async выходит за рамки MVC и также обрабатывает HTTP-запросы.

2. Maven Зависимости

Чтобы использовать асинхронную интеграцию в Spring MVC, нам необходимо включить следующие зависимости в нашpom.xml:


    org.springframework.security
    spring-security-web
    4.2.1.RELEASE


    org.springframework.security
    spring-security-config
    4.2.1.RELEASE

Последнюю версию зависимостей Spring Security можно найти вhere.

3. Spring MVC и@Async

Согласно официальномуdocs, Spring Security интегрируется сWebAsyncManager.

Первый шаг - убедиться, что нашspringSecurityFilterChain настроен для обработки асинхронных запросов. Мы можем сделать это либо в конфигурации Java, добавив следующую строку в наш класс конфигурацииServlet:

dispatcher.setAsyncSupported(true);

или в конфиге XML:


    springSecurityFilterChain
    org.springframework.web.filter.DelegatingFilterProxy
    true


    springSecurityFilterChain
    /*
    REQUEST
    ASYNC

Нам также необходимо включить параметрasync-supported в нашей конфигурации сервлета:


    ...
    true
    ...

Теперь мы готовы отправлять асинхронные запросы с распространенными с нимиSecurityContext.

Внутренние механизмы в Spring Security гарантируют, что нашSecurityContext больше не будет очищен, когда ответ будет зафиксирован в другомThread, что приведет к выходу пользователя из системы.

4. Случаи применения

Давайте посмотрим на это в действии на простом примере:

@Override
public Callable checkIfPrincipalPropagated() {
    Object before
      = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    log.info("Before new thread: " + before);

    return new Callable() {
        public Boolean call() throws Exception {
            Object after
              = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
            log.info("New thread: " + after);
            return before == after;
        }
    };
}

Мы хотим проверить, распространяется ли SpringSecurityContext на новый поток.

Представленный выше метод будет автоматически выполнятьCallable с включеннымиSecurityContext, как видно в журналах:

web - 2017-01-02 10:42:19,011 [http-nio-8081-exec-3] INFO
  o.example.web.service.AsyncService - Before new thread:
  [email protected]:
  Username: temporary; Password: [PROTECTED]; Enabled: true;
  AccountNonExpired: true; credentialsNonExpired: true;
  AccountNonLocked: true; Granted Authorities: ROLE_ADMIN

web - 2017-01-02 10:42:19,020 [MvcAsync1] INFO
  o.example.web.service.AsyncService - New thread:
  [email protected]:
  Username: temporary; Password: [PROTECTED]; Enabled: true;
  AccountNonExpired: true; credentialsNonExpired: true;
  AccountNonLocked: true; Granted Authorities: ROLE_ADMIN

Без настройки распространенияSecurityContext, второй запрос завершится значениемnull.

Есть и другие важные варианты использования асинхронных запросов с распространеннымSecurityContext:

  • мы хотим сделать несколько внешних запросов, которые могут выполняться параллельно и для выполнения которых может потребоваться значительное время

  • у нас есть некоторая значительная обработка, чтобы сделать локально, и наш внешний запрос может выполняться параллельно с этим

  • другие представляют сценарии «забей и забудь», например, отправив электронное письмо

Обратите внимание, что, если несколько вызовов наших методов были ранее объединены в синхронную форму, преобразование их в асинхронный подход может потребовать синхронизации результатов.

5. Заключение

В этом коротком руководстве мы проиллюстрировали поддержку Spring для обработки асинхронных запросов в аутентифицированном контексте..

С точки зрения модели программирования, новые возможности кажутся обманчиво простыми. Но, безусловно, есть некоторые аспекты, которые требуют более глубокого понимания.

Этот пример также доступен как проект Mavenover on Github.