Java 用@Configuration注释WebSecurityConfigureAdapter会导致配置被调用两次

Java 用@Configuration注释WebSecurityConfigureAdapter会导致配置被调用两次,java,spring,servlets,spring-security,spring-ioc,Java,Spring,Servlets,Spring Security,Spring Ioc,当我创建websecurityConfigureAdapter时,它会被注册两次。我创建的配置与参考文档中的Hello示例完全相同 尽管bean和过滤器链注册了两次,如tomcat输出所示,但配置仍按预期工作 00:32:01 INFO : Root WebApplicationContext: initialization started 00:32:01 INFO : Refreshing Root WebApplicationContext: startup date [Thu Oct 0

当我创建
websecurityConfigureAdapter
时,它会被注册两次。我创建的配置与参考文档中的Hello示例完全相同

尽管bean和过滤器链注册了两次,如tomcat输出所示,但配置仍按预期工作

00:32:01 INFO : Root WebApplicationContext: initialization started
00:32:01 INFO : Refreshing Root WebApplicationContext: startup date [Thu Oct 09 00:32:01 EST 2014]; root of context hierarchy
00:32:01 INFO : Registering annotated classes: [class com.acme.app.config.SecurityConfig]
______________
00:32:02 INFO : Creating filter chain: org.springframework.security.web.util.matcher.AnyRequestMatcher@1, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@26eed435, org.springframework.security.web.context.SecurityContextPersistenceFilter@cd9947, org.springframework.security.web.header.HeaderWriterFilter@354e8394, org.springframework.security.web.csrf.CsrfFilter@54fb62e3, org.springframework.security.web.authentication.logout.LogoutFilter@1df9cd96, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@4da923c6, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@1ee94efd, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@7d4b5722, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@26dde6a8, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@7c58b660, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@6fd12384, org.springframework.security.web.session.SessionManagementFilter@24d4cc75, org.springframework.security.web.access.ExceptionTranslationFilter@1709e92c, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@2c46f137]
00:32:02 INFO : Root WebApplicationContext: initialization completed in 565 ms
00:32:02 INFO : FrameworkServlet 'dispatcher': initialization started
00:32:02 INFO : Refreshing WebApplicationContext for namespace 'dispatcher-servlet': startup date [Thu Oct 09 00:32:02 EST 2014]; parent: Root WebApplicationContext
00:32:02 INFO : Registering annotated classes: [class com.acme.app.config.WebConfig]
______________
00:32:02 INFO : Creating filter chain: org.springframework.security.web.util.matcher.AnyRequestMatcher@1, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@52b26dcb, org.springframework.security.web.context.SecurityContextPersistenceFilter@10f4b535, org.springframework.security.web.header.HeaderWriterFilter@561ad054, org.springframework.security.web.csrf.CsrfFilter@4b6e4b62, org.springframework.security.web.authentication.logout.LogoutFilter@1d424a71, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@2dad3bf4, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@282a0f5c, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@52eeebb7, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@47d4a88e, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@474c5850, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@6fdc40f8, org.springframework.security.web.session.SessionManagementFilter@2978de23, org.springframework.security.web.access.ExceptionTranslationFilter@5bf190dd, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@22b3fc11]
AppInitializer.java

public class AppInitializer
        extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{SecurityConfig.class};
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{WebConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

}
@Configuration
@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        System.out.println("______________");
        auth
                .inMemoryAuthentication()
                .withUser("user").password("password").roles("USER");
    }

}
public class SecurityWebApplicationInitializer
        extends AbstractSecurityWebApplicationInitializer {

}
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.acme.app")
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**")
                .addResourceLocations("/static/");
    }

    @Bean
    public CookieLocaleResolver getLocaleResolver() {
        CookieLocaleResolver bean = new CookieLocaleResolver();
        bean.setCookieName("clientlanguage");
        bean.setCookieMaxAge(100000);
        return bean;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
        lci.setParamName("lang");
        registry.addInterceptor(lci);
    }

    @Bean
    public TilesConfigurer getTilesConfigurer() {
        CustomTilesInitializer ti = new CustomTilesInitializer();
        TilesConfigurer res = new TilesConfigurer();
        res.setCompleteAutoload(true);
        res.setDefinitions("/WEB-INF/**/tiles.xml");
        return res;
    }

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.tiles();
    }

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("home");
        registry.addRedirectViewController("/home", "/");
        registry.addViewController("/login").setViewName("login");
        registry.addViewController("/aboutme").setViewName("aboutme");
    }

}
SecurityWebApplicationInitializer.java

public class AppInitializer
        extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{SecurityConfig.class};
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{WebConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

}
@Configuration
@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        System.out.println("______________");
        auth
                .inMemoryAuthentication()
                .withUser("user").password("password").roles("USER");
    }

}
public class SecurityWebApplicationInitializer
        extends AbstractSecurityWebApplicationInitializer {

}
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.acme.app")
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**")
                .addResourceLocations("/static/");
    }

    @Bean
    public CookieLocaleResolver getLocaleResolver() {
        CookieLocaleResolver bean = new CookieLocaleResolver();
        bean.setCookieName("clientlanguage");
        bean.setCookieMaxAge(100000);
        return bean;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
        lci.setParamName("lang");
        registry.addInterceptor(lci);
    }

    @Bean
    public TilesConfigurer getTilesConfigurer() {
        CustomTilesInitializer ti = new CustomTilesInitializer();
        TilesConfigurer res = new TilesConfigurer();
        res.setCompleteAutoload(true);
        res.setDefinitions("/WEB-INF/**/tiles.xml");
        return res;
    }

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.tiles();
    }

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("home");
        registry.addRedirectViewController("/home", "/");
        registry.addViewController("/login").setViewName("login");
        registry.addViewController("/aboutme").setViewName("aboutme");
    }

}
WebConfig.java

public class AppInitializer
        extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{SecurityConfig.class};
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{WebConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

}
@Configuration
@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        System.out.println("______________");
        auth
                .inMemoryAuthentication()
                .withUser("user").password("password").roles("USER");
    }

}
public class SecurityWebApplicationInitializer
        extends AbstractSecurityWebApplicationInitializer {

}
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.acme.app")
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**")
                .addResourceLocations("/static/");
    }

    @Bean
    public CookieLocaleResolver getLocaleResolver() {
        CookieLocaleResolver bean = new CookieLocaleResolver();
        bean.setCookieName("clientlanguage");
        bean.setCookieMaxAge(100000);
        return bean;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
        lci.setParamName("lang");
        registry.addInterceptor(lci);
    }

    @Bean
    public TilesConfigurer getTilesConfigurer() {
        CustomTilesInitializer ti = new CustomTilesInitializer();
        TilesConfigurer res = new TilesConfigurer();
        res.setCompleteAutoload(true);
        res.setDefinitions("/WEB-INF/**/tiles.xml");
        return res;
    }

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.tiles();
    }

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("home");
        registry.addRedirectViewController("/home", "/");
        registry.addViewController("/login").setViewName("login");
        registry.addViewController("/aboutme").setViewName("aboutme");
    }

}
更新

正如@lkrnac所指出的,在
WebConfig
@ComponentScan
注释期间,配置被加载了两次。我认为spring足够聪明,不会加载它以前加载的配置。我可以在配置名称空间之外找到这些类,尽管这看起来很麻烦。相反,我选择提供一个排除过滤器,如下所示:

@ComponentScan(basePackages = "com.acme.app",
               excludeFilters = {
                   @Filter(type = ASSIGNABLE_TYPE,
                           value = {
                               WebConfig.class,
                               SecurityConfig.class
                           })
               })

这样,我仍然可以自由地为我的web上下文创建新的配置类,而不必担心它们是否被包含。

您的安全配置在package
com.acme.app.config.SecurityConfig
中。在
WebConfig
中扫描此软件包

所以我怀疑上下文被加载了两次:

  • 应用内初始值设定项
  • 通过WebConfig中的组件扫描
  • 尝试在
    com.acme.app
    package

    之外定位
    SecurityConfig
    匿名用户,指出此选项:

    @ComponentScan(basePackages = "com.acme.app",
                   excludeFilters = {
                      @Filter(type = FilterType.ANNOTATION, value = Configuration.class)
                   })
    

    你的web.xml看起来像什么呢?没有
    web.xml
    ,这就是为什么我要使用
    AbstractAnnotationConfigDispatcherServletInitializer
    事实上你是对的,我刚刚也发现了同样的事情。我认为spring会足够聪明,不会加载它已经加载的配置。解决方案隐藏了问题。扩展
    websecurityConfigureAdapter
    并使用
    @EnableWebMvcSecurity
    @Configuration
    类不是根应用程序上下文,因此从
    getRootConfigClasses()
    返回它是错误的设计。根上下文不应该包含任何与web相关的bean,
    getServletConfigClasses()
    就是专门为.Hmm设计的,有趣的是Spring安全文档使用了这种“糟糕的设计”。这里的例子:这将最终排除所有新的
    配置
    类,这可能是您不想要的。我知道,但作为替代方案,这很方便。例如,在我当前的应用程序中,为了对部分配置进行集成测试,我对每个包进行了组件扫描配置,并将它们全部显式导入到主包中。所以没有全局组件扫描。