2015-02-03 4 views
7

У меня есть веб-сервис 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 отсюда:

https://raw.githubusercontent.com/spring-projects/spring-security/rb3.2.5.RELEASE/web/src/main/java/org/springframework/security/web/context/AbstractSecurityWebApplicationInitializer.java

И я изменил метод 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) 

Это говорит о конфигурации безопасности происходит намного раньше в процессе загрузки, когда он работает против того, когда он терпит неудачу.

ответ

15

Я думаю, что у вас должен быть конкретный подкласс AbstractSecurityWebApplicationInitializer в вашем приложении. Spring Servlet 3.0 поддерживает эту реализацию WebApplicationInitializer и называет ее, когда Tomcat запускает ваше приложение. Это вызывает попытку зарегистрировать фильтр Spring Security. У вас также есть класс WebXml, который распространяется на SpringBootServletInitializer. Это тоже WebApplicationInitializer, который будет вызван, когда Tomcat начнет ваше приложение. Благодаря поддержке автоматической настройки Spring Boot это также запускает попытку зарегистрировать фильтр Spring Security.

Ваш класс WebXml не объявляет о заказе (он не использует интерфейс Spring Ordered и не аннотируется @Order). Я бы предположил, что то же самое относится к вашему подклассу AbstractSecurityWebApplicationInitializer. Это означает, что оба они имеют одинаковый порядок (по умолчанию), поэтому Spring может называть их в любом порядке.Ваше приложение работает, когда ваш подкласс AbstractSecurityWebApplicationInitializer идет первым, поскольку Spring Boot терпим к уже существующему фильтру. Если сбой, когда Spring Boot идет первым, поскольку AbstractSecurityWebApplicationInitializer не так толерантен.

Сказав все это, поскольку вы используете Spring Boot, вам может и не понадобиться ваш AbstractSecurityWebApplicationInitializer, поэтому простейшим решением, вероятно, является его удаление. Если вам do, то вам необходимо назначить его и WebXml заказ (аннотация с помощью @Order или осуществить Ordered), так что WebXml будет всегда вызываться после вашего AbstractSecurityWebApplicationInitializer подкласса.

0

После пружинный загрузки документации следует отключить конфигурацию безопасности по умолчанию, нагруженный пружинным загрузки путем добавления аннотаций @EnableWebMvcSecurity в конфигурации приложения (см 75.2 Change the AuthenticationManager and add user accounts) и чем вы должны настроить адаптер веб-безопасности, как это:

@Bean 
WebSecurityConfigurerAdapter webSecurityAdapter() { 
    WebSecurityConfigurerAdapter adapter = new WebSecurityConfigurerAdapter() { 
      @Override 
      protected void configure(HttpSecurity http) throws Exception { 
       http.... 

 Смежные вопросы

  • Нет связанных вопросов^_^