Фильтр загрузки Spring вызывается дважды или вообще не вызывается

#java #spring #spring-boot #spring-security

#java #весна #spring-boot #spring-безопасность

Вопрос:

Я реализовал пользовательский фильтр, который добавляет что-то из файлов cookie запроса в его заголовки :

 @Component
@Slf4j
public class MyCustomFilter implements Filter {

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws ServletException, IOException {
        .... some logic...
        log.info("Sending request to next chain for validation..");
        chain.doFilter(request, response);
        log.info("Authentication completed sucessfully");
    }

    @Bean
    // This method is needed to replace the default cookieFilter.json processor of tomcat that ignores the jwt cookieFilter.json
    public WebServerFactoryCustomizer<TomcatServletWebServerFactory> cookieProcessorCustomizer() {
        return tomcatServletWebServerFactory -> tomcatServletWebServerFactory.addContextCustomizers((TomcatContextCustomizer) context -> {
            context.setCookieProcessor(new LegacyCookieProcessor());
        });
    }

}
  

Мой класс WebSecurityConfigurerAdapter :

 @Configuration
public class AuthSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        //configuring strategy
        http.sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .authorizeRequests()
                .anyRequest().authenticated().and()
                .oauth2ResourceServer().jwt().and();
        http.csrf().disable();
        http.addFilterBefore(new MyCustomFilter (), UsernamePasswordAuthenticationFilter.class);
        http.exceptionHandling().authenticationEntryPoint(new AuthExceptionEntryPoint());
    }

}
  

Когда я запускаю код и отправляю запрос через postman / curl, я вижу, что фильтр срабатывает дважды в

 Sending request to next chain for validation..
Sending request to next chain for validation..
Authentication completed sucessfully
Authentication completed sucessfully
  

Я нашел несколько сообщений о проблеме и попробовал следующие решения :

  1. Это происходит потому, что spring автоматически регистрирует компоненты, и я добавляю фильтр вручную в методе configure. Поэтому я удалил добавление фильтра вручную в метод configure(). В результате фильтр вообще не вызывался.

  2. Вместо реализации интерфейса фильтра попробуйте расширить OncePerRequestFilter класс. Сделано, но фильтр все равно срабатывает дважды.

  3. Пытался также удалить @Component аннотацию и добавить фильтр вручную. Кроме того, мне пришлось переместить CookieProcessor компонент в класс конфигурации. Проблема, возникшая впоследствии, заключается в том, что приложение не запускается из-за следующей ошибки :

    Вызвано: org.springframework.beans.Исключение BeanInstantiationException: не удалось создать экземпляр [org.springframework.web.servlet .HandlerMapping]: Фабричный метод ‘resourceHandlerMapping’ выдал исключение; вложенным исключением является java.lang.Исключение IllegalStateException: нет набора ServletContext

Я использую spring-security версии 5.3.3.

Комментарии:

1. Удалить @Component . В настоящее время он является частью обычной цепочки фильтров и цепочки фильтров безопасности, поэтому он выполняется дважды (он также регистрируется дважды). @Bean Метод должен быть в @Configuration классе, а не в этом @Component .

2. Я переместил компонент в конфигурацию и получаю следующую ошибку: Вызвано: org.springframework.beans. Исключение BeanInstantiationException: не удалось создать экземпляр [org.springframework.web.servlet . HandlerMapping]: Фабричный метод ‘resourceHandlerMapping’ выдал исключение; вложенным исключением является java.lang. Исключение IllegalStateException: нет набора ServletContext

3. Верните a TomcatContextCustomizer и не добавляйте его. Вы слишком усложняете ситуацию.

4. Я использовал следующий код, и я все еще получаю ту же ошибку: public TomcatContextCustomizer cookieProcessorCustomizer() { возвращает контекст -> context.setCookieProcessor(новый LegacyCookieProcessor()); }

Ответ №1:

Как правило, не добавляйте @Bean методы в @Component классы, поскольку они обрабатываются иначе, чем в @Configuration классах. (Смотрите это).

Ваш код в @Bean слишком сложен. Создайте и верните a TomcatContextCustomizer для внесения изменений. Ваш код приведет к циклическим ссылкам, что приведет к ошибкам инициализации.

Добавьте следующий @Bean метод в свой @SpringBootApplication аннотированный класс

 @Bean
public TomactContextCustomizer cookieProcessorCustomizer() {
  return (context) -> context.setCookieProcessor(new LegacyCookieProcessor());
}
  

Теперь в вашем Filter либо удалите, @Component либо добавьте дополнение, FilterRegistrationBean чтобы предотвратить его добавление в обычную цепочку фильтров. (Spring Boot автоматически регистрирует все обнаруженные Filter экземпляры в обычной цепочке фильтров).

 @Bean
public FilterRegistrationBean<MyFilter> myFilterRegistrationBean(MyFilter myFilter) {
  FilterRegistrationBean<MyFilter> frb = new FilterRegistrationBean<>(myFilter);
  frb.setEnabled(false);
  return frb;
}
  

Если вы удалите @Component приведенный выше фрагмент, он не нужен, если вы этого не сделаете, вам следует повторно использовать отсканированный MyFilter экземпляр в своей конфигурации безопасности.

 @Configuration
public class AuthSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private MyFilter myFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        //configuring strategy
        http.sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .authorizeRequests()
                .anyRequest().authenticated().and()
                .oauth2ResourceServer().jwt().and();
        http.csrf().disable();
        http.addFilterBefore(myFilter, UsernamePasswordAuthenticationFilter.class);
        http.exceptionHandling().authenticationEntryPoint(new AuthExceptionEntryPoint());
    }

}
  

Комментарии:

1. Прежде всего, спасибо за четкое объяснение. Я удалил аннотацию @Component и поэтому вместо передачи введенного компонента я просто создал новый экземпляр своего фильтра, который я передаю addFilterBefore(..). Я попытался запустить его, но получаю ту же ошибку:(

2. Вам также нужно TomcatContextCustomizer LegacyCookieProcessor , чтобы он не был зарегистрирован.

3. Я не писал его, но я добавил компонент TomactContextCustomizer в свой класс @Configuraiton

4. Когда я запускаю код без метода tomcatContextCustomizer, запускается приложение spring, но мои файлы cookie игнорируются. С помощью метода @Bean настройщика приложение spring не запускается из-за ошибки контекста, о которой я упоминал ранее

5. Решением было переместить TomcatContextCustomizer в другой класс @Configuration и не сохранять его в классе, который расширяет WebSecurityConfigurerAdapter . Спасибо за всю помощь!!