У меня есть веб-сервис REST, реализованный с Spring Boot 1.2.0-RELEASE, который иногда вызывает при запуске следующее исключение.Видимая пружина Весенняя гонка состояния, вызывающая повторяющуюся пружинуSecurityFilterChain registration
03-Feb-2015 11:42:23.697 SEVERE [localhost-startStop-1] org.apache.catalina.core.ContainerBase.addChildInternal ContainerBase.addChild: start:
org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[]]
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:154)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:725)
...
Caused by: java.lang.IllegalStateException: Duplicate Filter registration for 'springSecurityFilterChain'. Check to ensure the Filter is only configured once.
at org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer.registerFilter(AbstractSecurityWebApplicationInitializer.java:215)
at org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer.insertSpringSecurityFilterChain(AbstractSecurityWebApplicationInitializer.java:147)
...
Когда я говорю «иногда», я имею в виду просто перезапустить сервер Tomcat (версия 8.0.17) будет давать либо это исключение или будет успешно загрузить без проблем.
Это приложение Servlet 3.0, созданное на Spring Boot, поэтому у нас нет традиционного файла web.xml. Вместо этого мы инициализируем наш сервлет с помощью Java.
package com.v.dw.webservice;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;
public class WebXml extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(ApplicationConfig.class);
}
}
Мы также использовать команду mvn spring-boot:run
в процессе разработки, и это состояние гонки еще появляться при запуске на этом пути. «Корень» наша конфигурации и основной метод, используемый мавенна в том же классе:
package com.v.dw.webservice;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.actuate.autoconfigure.ManagementSecurityAutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
@SpringBootApplication
@EnableAutoConfiguration(exclude = {ManagementSecurityAutoConfiguration.class, SecurityAutoConfiguration.class})
public class ApplicationConfig {
public static void main(String[] args) {
SpringApplication.run(ApplicationConfig.class, args);
}
@Value("${info.build.version}")
private String apiVersion;
@Bean
@Primary
@ConfigurationProperties(prefix="datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
}
Я попытался упростить нашу логику аутентификации использовать пользовательский в памяти поставщика проверки подлинности для тестирования. Насколько я могу судить, это единственный пользовательский поставщик проверки подлинности в пути к классам, и мы не импортируем какие-либо классы конфигурации вне корневого пакета приложения.
К сожалению, выход каротаж обеспечивается Spring и Tomcat не помогает обеспечить любой контекст вокруг ошибки, поэтому я попытался схватить источник AbstractSecurityWebApplictionInitializer отсюда:
И я изменил метод registerFilter(...)
в попытайтесь сгенерировать полезный отладочный вывод, добавив System.out
вызовов.
private final void registerFilter(ServletContext servletContext, boolean insertBeforeOtherFilters, String filterName, Filter filter) {
System.out.println(">>>>>> Registering filter '" + filterName + "' with: " + filter.getClass().toString());
Dynamic registration = servletContext.addFilter(filterName, filter);
if(registration == null) {
System.out.println(">>>>>> Existing filter '" + filterName + "' as: " + servletContext.getFilterRegistration(filterName).getClassName());
throw new IllegalStateException("Duplicate Filter registration for '" + filterName +"'. Check to ensure the Filter is only configured once.");
}
registration.setAsyncSupported(isAsyncSecuritySupported());
EnumSet<DispatcherType> dispatcherTypes = getSecurityDispatcherTypes();
registration.addMappingForUrlPatterns(dispatcherTypes, !insertBeforeOtherFilters, "/*");
}
Когда он выходит из строя, выход отладки генерируется только один раз перед исключением. Это указывает на метод registerFilter(...)
вызывается только один раз, и относительно поздно в процессе Спринг загрузки:
>>>>>> Registering filter 'springSecurityFilterChain' with: class org.springframework.web.filter.DelegatingFilterProxy
>>>>>> Existing filter 'springSecurityFilterChain' as: org.springframework.security.web.FilterChainProxy
Когда он работает, отладочный вывод выглядит следующим образом:
>>>>>> Registering filter 'springSecurityFilterChain' with: class org.springframework.web.filter.DelegatingFilterProxy
. ____ _ __ _ _
/\\/___'_ __ _ _(_)_ __ __ _ \ \ \ \
(()\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ))))
' |____| .__|_| |_|_| |_\__, |////
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.2.0.RELEASE)
Это говорит о конфигурации безопасности происходит намного раньше в процессе загрузки, когда он работает против того, когда он терпит неудачу.