#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
Я нашел несколько сообщений о проблеме и попробовал следующие решения :
-
Это происходит потому, что spring автоматически регистрирует компоненты, и я добавляю фильтр вручную в методе configure. Поэтому я удалил добавление фильтра вручную в метод configure(). В результате фильтр вообще не вызывался.
-
Вместо реализации интерфейса фильтра попробуйте расширить
OncePerRequestFilter
класс. Сделано, но фильтр все равно срабатывает дважды. -
Пытался также удалить
@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 . Спасибо за всю помощь!!