Поддержка 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.