From 6b42a2eae1b6de5edf2fa51e65e965b0419c596e Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Mon, 3 Feb 2014 13:21:31 -0600 Subject: [PATCH] SEC-2461: Multi WebSecurityConfiguration does not create null springSecurityFilterChain --- ...edWebSecurityConfigurersIgnoreParents.java | 58 +++++++++++++++++++ .../WebSecurityConfiguration.java | 15 ++--- .../WebSecurityConfigurationTests.groovy | 57 +++++++++++++----- .../sec2377/b/Sec2377BConfig.java | 5 +- 4 files changed, 112 insertions(+), 23 deletions(-) create mode 100644 config/src/main/java/org/springframework/security/config/annotation/web/configuration/AutowiredWebSecurityConfigurersIgnoreParents.java diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configuration/AutowiredWebSecurityConfigurersIgnoreParents.java b/config/src/main/java/org/springframework/security/config/annotation/web/configuration/AutowiredWebSecurityConfigurersIgnoreParents.java new file mode 100644 index 0000000000..1e034739c0 --- /dev/null +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configuration/AutowiredWebSecurityConfigurersIgnoreParents.java @@ -0,0 +1,58 @@ +/* + * Copyright 2002-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.security.config.annotation.web.configuration; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import javax.servlet.Filter; + +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.security.config.annotation.SecurityConfigurer; +import org.springframework.security.config.annotation.web.WebSecurityConfigurer; +import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.util.Assert; + + +/** + * A class used to get all the {@link WebSecurityConfigurer} instances from the + * current {@link ApplicationContext} but ignoring the parent. + * + * @author Rob Winch + * + */ +final class AutowiredWebSecurityConfigurersIgnoreParents { + + private final ConfigurableListableBeanFactory beanFactory; + + public AutowiredWebSecurityConfigurersIgnoreParents(ConfigurableListableBeanFactory beanFactory) { + Assert.notNull(beanFactory,"beanFactory cannot be null"); + this.beanFactory = beanFactory; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public List> getWebSecurityConfigurers() { + List> webSecurityConfigurers = new ArrayList>(); + Map beansOfType = beanFactory.getBeansOfType(WebSecurityConfigurer.class); + for(Entry entry : beansOfType.entrySet()) { + webSecurityConfigurers.add(entry.getValue()); + } + return webSecurityConfigurers; + } +} \ No newline at end of file diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configuration/WebSecurityConfiguration.java b/config/src/main/java/org/springframework/security/config/annotation/web/configuration/WebSecurityConfiguration.java index c872eca045..0a9a50ff85 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configuration/WebSecurityConfiguration.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configuration/WebSecurityConfiguration.java @@ -23,6 +23,8 @@ import javax.servlet.Filter; import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.DependsOn; @@ -34,7 +36,6 @@ import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.annotation.Order; import org.springframework.core.type.AnnotationMetadata; import org.springframework.security.access.expression.SecurityExpressionHandler; -import org.springframework.security.config.annotation.AlreadyBuiltException; import org.springframework.security.config.annotation.ObjectPostProcessor; import org.springframework.security.config.annotation.SecurityConfigurer; import org.springframework.security.config.annotation.web.WebSecurityConfigurer; @@ -88,11 +89,7 @@ public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAwa if(!hasConfigurers) { throw new IllegalStateException("At least one non-null instance of "+ WebSecurityConfigurer.class.getSimpleName()+" must be exposed as a @Bean when using @EnableWebSecurity. Hint try extending "+ WebSecurityConfigurerAdapter.class.getSimpleName()); } - try { - return webSecurity.build(); - } catch (AlreadyBuiltException e) { - return webSecurity.getObject(); - } + return webSecurity.build(); } /** @@ -115,7 +112,7 @@ public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAwa */ @Autowired(required = false) public void setFilterChainProxySecurityConfigurer(ObjectPostProcessor objectPostProcessor, - List> webSecurityConfigurers) throws Exception { + @Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List> webSecurityConfigurers) throws Exception { webSecurity = objectPostProcessor.postProcess(new WebSecurity(objectPostProcessor)); if(debugEnabled != null) { webSecurity.debug(debugEnabled); @@ -137,6 +134,10 @@ public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAwa this.webSecurityConfigurers = webSecurityConfigurers; } + @Bean + public AutowiredWebSecurityConfigurersIgnoreParents autowiredWebSecurityConfigurersIgnoreParents(ConfigurableListableBeanFactory beanFactory) { + return new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory); + } /** * A custom verision of the Spring provided AnnotationAwareOrderComparator diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/configuration/WebSecurityConfigurationTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/web/configuration/WebSecurityConfigurationTests.groovy index 864d6f429a..e6a4aba443 100644 --- a/config/src/test/groovy/org/springframework/security/config/annotation/web/configuration/WebSecurityConfigurationTests.groovy +++ b/config/src/test/groovy/org/springframework/security/config/annotation/web/configuration/WebSecurityConfigurationTests.groovy @@ -17,31 +17,28 @@ package org.springframework.security.config.annotation.web.configuration; import static org.junit.Assert.* -import java.util.List; - import org.springframework.beans.factory.BeanCreationException -import org.springframework.context.ApplicationListener; -import org.springframework.context.annotation.Bean; +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.context.annotation.AnnotationConfigApplicationContext +import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.core.annotation.Order -import org.springframework.expression.ExpressionParser; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.security.access.expression.SecurityExpressionHandler; +import org.springframework.expression.ExpressionParser +import org.springframework.mock.web.MockHttpServletRequest +import org.springframework.security.access.expression.SecurityExpressionHandler import org.springframework.security.authentication.AuthenticationManager -import org.springframework.security.authentication.event.AuthenticationSuccessEvent; import org.springframework.security.config.annotation.BaseSpringSpec import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder import org.springframework.security.config.annotation.web.builders.HttpSecurity import org.springframework.security.config.annotation.web.builders.WebSecurity import org.springframework.security.web.FilterChainProxy import org.springframework.security.web.SecurityFilterChain -import org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator; -import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator; -import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler; -import org.springframework.security.web.access.expression.WebSecurityExpressionHandler; -import org.springframework.security.web.access.intercept.FilterSecurityInterceptor; +import org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator +import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator +import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler +import org.springframework.security.web.access.expression.WebSecurityExpressionHandler import org.springframework.security.web.util.matcher.AnyRequestMatcher -import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.test.util.ReflectionTestUtils /** * @author Rob Winch @@ -313,4 +310,36 @@ class WebSecurityConfigurationTests extends BaseSpringSpec { } } } + + def "SEC-2461: Multiple WebSecurityConfiguration instances cause null springSecurityFilterChain"() { + setup: + def parent = loadConfig(ParentConfig) + def child = new AnnotationConfigApplicationContext() + child.register(ChildConfig) + child.parent = parent + when: + child.refresh() + then: "springSecurityFilterChain can be found in parent and child" + parent.getBean("springSecurityFilterChain") + child.getBean("springSecurityFilterChain") + and: "springSecurityFilterChain is defined in both parent and child (don't search parent)" + parent.containsBeanDefinition("springSecurityFilterChain") + child.containsBeanDefinition("springSecurityFilterChain") + cleanup: + child?.close() + // parent.close() is in superclass + } + + @EnableWebSecurity + @Configuration + static class ParentConfig extends WebSecurityConfigurerAdapter { + @Autowired + public void configureGlobal(AuthenticationManagerBuilder auth) { + auth.inMemoryAuthentication() + } + } + + @EnableWebSecurity + @Configuration + static class ChildConfig extends WebSecurityConfigurerAdapter { } } diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/configuration/sec2377/b/Sec2377BConfig.java b/config/src/test/groovy/org/springframework/security/config/annotation/web/configuration/sec2377/b/Sec2377BConfig.java index 8c3d1d66c7..26c0019ab0 100644 --- a/config/src/test/groovy/org/springframework/security/config/annotation/web/configuration/sec2377/b/Sec2377BConfig.java +++ b/config/src/test/groovy/org/springframework/security/config/annotation/web/configuration/sec2377/b/Sec2377BConfig.java @@ -17,9 +17,10 @@ package org.springframework.security.config.annotation.web.configuration.sec2377 import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @EnableWebSecurity @Configuration -public class Sec2377BConfig { +public class Sec2377BConfig extends WebSecurityConfigurerAdapter { -} +} \ No newline at end of file