Как работает Spring Security и как устроена цепочка фильтров
Что проверяют
Понимаете ли вы, как запрос попадает в Spring Security и где принимается решение пустить или отказать. Следом просят встроить свой фильтр для JWT и объяснить, какой компонент отвечает 401, а какой 403.
Как отвечать
Схема входа запроса:
DelegatingFilterProxy: сервлетный фильтр, передающий управление Spring-бинуspringSecurityFilterChain;FilterChainProxy: этот бин держит списокSecurityFilterChainи выбирает первую цепочку, чей матчер подошёл к запросу. Так у/api/**может быть токен, а у админки форма логина;- цепочка фильтров: каждый фильтр делает одну задачу и передаёт запрос дальше или обрывает обработку.
Ключевые фильтры в Spring Security 6 по порядку: SecurityContextHolderFilter, CsrfFilter, LogoutFilter, фильтры аутентификации (UsernamePasswordAuthenticationFilter, BearerTokenAuthenticationFilter), ExceptionTranslationFilter и в конце AuthorizationFilter.
Аутентификация: фильтр собирает из запроса Authentication и отдаёт его в AuthenticationManager (обычно ProviderManager), тот перебирает AuthenticationProvider. Для логина и пароля это DaoAuthenticationProvider с UserDetailsService и PasswordEncoder. Результат ложится в SecurityContextHolder, по умолчанию в ThreadLocal.
Ошибки: ExceptionTranslationFilter ловит AuthenticationException и зовёт AuthenticationEntryPoint (401 или редирект на логин), а AccessDeniedException отдаёт в AccessDeniedHandler (403).
Конфигурация сейчас это бин SecurityFilterChain с лямбда-DSL над HttpSecurity, свой фильтр вставляют через addFilterBefore. Реальный список фильтров покажет логгер org.springframework.security на уровне TRACE.
Где спотыкаются
Рассказывают про наследование от WebSecurityConfigurerAdapter. Его объявили устаревшим в 5.7 и удалили в 6.0, такой ответ выдаёт старый опыт.
Помечают свой фильтр @Component: Spring Boot регистрирует его ещё и в сервлетном контейнере, и он срабатывает дважды. Лечится через FilterRegistrationBean с setEnabled(false).
Отключают CSRF «потому что мешает» при сессии на куках. Для API с токеном в заголовке это оправдано, для формы логина нет.