Как работают фильтры (Filters) в Spring Security и как создавать собственные?

«Как работают фильтры (Filters) в Spring Security и как создавать собственные?» — вопрос из категории Spring, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В Spring Security фильтры Servlet образуют цепочку, которая обрабатывает каждый HTTP-запрос до того, как он достигнет контроллера. Они отвечают за аутентификацию, авторизацию, защиту от атак и другую сквозную логику.

Ключевые встроенные фильтры:

  • SecurityContextPersistenceFilter: Восстанавливает SecurityContext (информацию об аутентификации) из сессии между запросами.
  • UsernamePasswordAuthenticationFilter: Обрабатывает форму логина (POST /login).
  • BasicAuthenticationFilter: Обрабатывает HTTP Basic Authentication.
  • FilterSecurityInterceptor: Принимает окончательное решение о доступе к защищенному ресурсу на основе конфигурации (authorizeRequests()).

Создание и регистрация кастомного фильтра:

  1. Реализация фильтра: Наследуйтесь от OncePerRequestFilter, чтобы гарантировать однократное выполнение за запрос.
    @Component
    public class CustomLoggingFilter extends OncePerRequestFilter {
        @Override
        protected void doFilterInternal(HttpServletRequest request,
                                        HttpServletResponse response,
                                        FilterChain filterChain) throws ServletException, IOException {
            long startTime = System.currentTimeMillis();
            // Логика ДО прохождения запроса дальше по цепочке
            filterChain.doFilter(request, response); // Пропускаем запрос дальше
            // Логика ПОСЛЕ прохождения цепочки (когда ответ уже сформирован)
            long duration = System.currentTimeMillis() - startTime;
            logger.info("Request {} completed in {} ms", request.getRequestURI(), duration);
        }
    }
  2. Встраивание в цепочку Security: Укажите позицию фильтра относительно других.

    @Configuration
    @EnableWebSecurity
    public class SecurityConfig {
        @Autowired
        private CustomLoggingFilter customLoggingFilter;
    
        @Bean
        public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
            http
                .addFilterBefore(customLoggingFilter, UsernamePasswordAuthenticationFilter.class)
                // .addFilterAfter(filter, existingFilterClass)
                // .addFilterAt(filter, existingFilterClass) // заменяет позицию
                .authorizeRequests()
                // ... остальная конфигурация
            return http.build();
        }
    }

    Почему это важно: Понимание цепочки фильтров позволяет правильно добавлять логирование, проверку заголовков, кастомную аутентификацию (например, по JWT) и другие cross-cutting concerns.