1. Введение
В этом кратком руководстве мы сосредоточимся на поддержке Servlet 3 для асинхронных запросов и на том, как Spring MVC и Spring Security обрабатывают эти файлы .
Основной мотивацией асинхронности в веб-приложениях является обработка длительных запросов. В большинстве случаев нам нужно убедиться, что принципал безопасности Spring распространяется на эти потоки.
И, конечно же, Spring Security интегрируется с @Async
за рамками MVC и также обрабатывает HTTP-запросы.
2. Зависимости Maven
Чтобы использовать асинхронную интеграцию в Spring MVC, нам нужно включить следующие зависимости в наш pom.xml
:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>5.6.0</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>5.6.0</version>
</dependency>
Последнюю версию зависимостей Spring Security можно найти здесь .
3. Spring MVC и @Async
Согласно официальной документации , Spring Security интегрируется с WebAsyncManager
.
Первый шаг — убедиться, что наш springSecurityFilterChain
настроен для обработки асинхронных запросов. Мы можем сделать это либо в конфигурации Java, добавив следующую строку в наш класс конфигурации сервлета :
dispatcher.setAsyncSupported(true);
или в конфигурации XML:
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<async-supported>true</async-supported>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ASYNC</dispatcher>
</filter-mapping>
Нам также необходимо включить параметр async-support
в нашей конфигурации сервлета:
<servlet>
...
<async-supported>true</async-supported>
...
</servlet>
Теперь мы готовы отправлять асинхронные запросы с распространяемым вместе с ними SecurityContext .
Внутренние механизмы в Spring Security гарантируют, что наш SecurityContext
больше не будет очищаться, когда ответ фиксируется в другом потоке
, что приводит к выходу пользователя из системы.
4. Варианты использования
Давайте посмотрим это в действии на простом примере:
@Override
public Callable<Boolean> checkIfPrincipalPropagated() {
Object before
= SecurityContextHolder.getContext().getAuthentication().getPrincipal();
log.info("Before new thread: " + before);
return new Callable<Boolean>() {
public Boolean call() throws Exception {
Object after
= SecurityContextHolder.getContext().getAuthentication().getPrincipal();
log.info("New thread: " + after);
return before == after;
}
};
}
Мы хотим проверить, распространяется ли Spring SecurityContext
на новый поток.
Представленный выше метод будет автоматически выполнять свой Callable
с включенным SecurityContext
, как видно из журналов:
web - 2017-01-02 10:42:19,011 [http-nio-8081-exec-3] INFO
o.foreach.web.service.AsyncService - Before new thread:
org.springframework.security.core.userdetails.User@76507e51:
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.foreach.web.service.AsyncService - New thread:
org.springframework.security.core.userdetails.User@76507e51:
Username: temporary; Password: [PROTECTED]; Enabled: true;
AccountNonExpired: true; credentialsNonExpired: true;
AccountNonLocked: true; Granted Authorities: ROLE_ADMIN
Без настройки распространения SecurityContext
второй запрос будет иметь нулевое
значение.
Есть и другие важные варианты использования асинхронных запросов с распространяемым SecurityContext
:
- мы хотим сделать несколько внешних запросов, которые могут выполняться параллельно и выполнение которых может занять значительное время.
- у нас есть некоторая значительная обработка, которую нужно выполнить локально, и наш внешний запрос может выполняться параллельно этому
- другие представляют сценарии «выстрелил и забыл», например, отправка электронного письма
Обратите внимание, что если наши несколько вызовов методов ранее были связаны синхронно, преобразование их в асинхронный подход может потребовать синхронизации результатов.
5. Вывод
В этом коротком руководстве мы проиллюстрировали поддержку Spring для обработки асинхронных запросов в аутентифицированном контексте .
С точки зрения модели программирования новые возможности кажутся обманчиво простыми. Но есть, конечно, некоторые аспекты, которые требуют более глубокого понимания.
Этот пример также доступен в виде проекта Maven на Github .