Перейти к основному содержимому

Асинхронная поддержка Servlet 3 с Spring MVC и Spring Security

· 3 мин. чтения

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 .