Метод помечен @Async, и внутри него SecurityContextHolder.getContext().getAuthentication() возвращает null. Почему?
- Как передать пользователя в @Async-метод?
SecurityContext хранится в ThreadLocal потока запроса и в асинхронный поток сам не переезжает. Решение — DelegatingSecurityContextAsyncTaskExecutor (или TaskDecorator), который копирует контекст.
- То же для MDC с trace id — TaskDecorator
- Режим MODE_INHERITABLETHREADLOCAL не подходит для пулов — потоки переиспользуются
- Виртуальные потоки не меняют проблему
А ещё Context Propagation (Micrometer) переносит контекст автоматически
Подробно
Стратегия хранения по умолчанию — MODE_THREADLOCAL, поэтому любой переход в другой поток теряет контекст: это касается @Async, задач в собственном пуле и реактивных цепочек. Варианты решения: включить MODE_INHERITABLETHREADLOCAL, что работает для потоков, созданных напрямую, но бесполезно для пула, где потоки существовали заранее; либо, что правильнее, обернуть исполнитель в DelegatingSecurityContextExecutor или DelegatingSecurityContextAsyncTaskExecutor, который явно переносит контекст в задачу. В WebFlux механизм другой — контекст живёт в Reactor Context, а не в ThreadLocal. И совсем простой вариант: передать нужные данные о пользователе параметрами метода, не полагаясь на неявное состояние.
Что хотят услышать
- Причина: ThreadLocal.
- Правильное решение с executor'ом.
- Почему не inheritable для пулов.
Частые ошибки
- Рассчитывать, что MODE_INHERITABLETHREADLOCAL решит проблему для пула потоков.
- Полагаться на SecurityContextHolder внутри реактивных цепочек.
Проверьте себя
Один верный вариант. Подумайте, потом откройте ответ.
Показать варианты и ответ
- ✗@Async несовместим со Spring Security, и аутентификация в таком методе всегда равна null
- ✓Контекст лежит в ThreadLocal и не переходит в поток @Async без делегирующего executor
- ✗Аутентификация истекает при выходе из метода контроллера
- ✗Нужно пометить метод дополнительно аннотацией @PreAuthorize
Что спросят дальше
- Почему наследуемый ThreadLocal не помогает с пулом?
- Как устроено распространение контекста в WebFlux?
Разбор темы
- Spring Security: фильтр-цепочка и авторизация
SecurityFilterChain, аутентификация (UserDetailsService/PasswordEncoder), SecurityContext, авторизация запросов и CSRF/CORS/сессии.
Связанные вопросы
Разбор собеседования с sotto
После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.