2
0
Эх сурвалжийг харах

WebSecurityConfigurationTests groovy->java

Issue: gh-4939
Joe Grandja 7 жил өмнө
parent
commit
c922fe3be1

+ 0 - 344
config/src/test/groovy/org/springframework/security/config/annotation/web/configuration/WebSecurityConfigurationTests.groovy

@@ -1,344 +0,0 @@
-/*
- * 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.lang.reflect.Modifier
-
-import static org.junit.Assert.*
-
-import org.springframework.beans.factory.BeanCreationException
-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.security.authentication.AuthenticationManager
-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.config.annotation.web.configuration.WebSecurityConfigurationTests.DuplicateOrderConfig;
-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.util.matcher.AnyRequestMatcher
-import org.springframework.test.util.ReflectionTestUtils
-
-/**
- * @author Rob Winch
- *
- */
-class WebSecurityConfigurationTests extends BaseSpringSpec {
-
-	def "WebSecurityConfigurers are sorted"() {
-		when:
-			loadConfig(SortedWebSecurityConfigurerAdaptersConfig);
-			List<SecurityFilterChain> filterChains = context.getBean(FilterChainProxy).filterChains
-		then:
-			filterChains[0].requestMatcher.pattern == "/ignore1"
-			filterChains[0].filters.empty
-			filterChains[1].requestMatcher.pattern == "/ignore2"
-			filterChains[1].filters.empty
-
-			filterChains[2].requestMatcher.pattern == "/role1/**"
-			filterChains[3].requestMatcher.pattern == "/role2/**"
-			filterChains[4].requestMatcher.pattern == "/role3/**"
-			filterChains[5].requestMatcher.class == AnyRequestMatcher
-	}
-
-
-	@EnableWebSecurity
-	static class SortedWebSecurityConfigurerAdaptersConfig {
-		public AuthenticationManager authenticationManager() throws Exception {
-			return new AuthenticationManagerBuilder()
-				.inMemoryAuthentication()
-					.withUser("marissa").password("koala").roles("USER").and()
-					.withUser("paul").password("emu").roles("USER").and()
-					.and()
-				.build();
-		}
-
-		@Configuration
-		@Order(1)
-		public static class WebConfigurer1 extends WebSecurityConfigurerAdapter {
-			@Override
-			public void configure(WebSecurity web)	throws Exception {
-				web
-					.ignoring()
-						.antMatchers("/ignore1","/ignore2");
-			}
-
-			@Override
-			protected void configure(HttpSecurity http) throws Exception {
-				http
-					.antMatcher("/role1/**")
-					.authorizeRequests()
-						.anyRequest().hasRole("1");
-			}
-		}
-
-		@Configuration
-		@Order(2)
-		public static class WebConfigurer2 extends WebSecurityConfigurerAdapter {
-			@Override
-			protected void configure(HttpSecurity http) throws Exception {
-				http
-					.antMatcher("/role2/**")
-						.authorizeRequests()
-							.anyRequest().hasRole("2");
-			}
-		}
-
-		@Configuration
-		@Order(3)
-		public static class WebConfigurer3 extends WebSecurityConfigurerAdapter {
-			@Override
-			protected void configure(HttpSecurity http) throws Exception {
-				http
-					.antMatcher("/role3/**")
-					.authorizeRequests()
-						.anyRequest().hasRole("3");
-			}
-		}
-
-		@Configuration
-		public static class WebConfigurer4 extends WebSecurityConfigurerAdapter {
-
-			@Override
-			protected void configure(HttpSecurity http) throws Exception {
-				http
-					.authorizeRequests()
-						.anyRequest().hasRole("4");
-			}
-		}
-	}
-
-	def "WebSecurityConfigurers fails with duplicate order"() {
-		when:
-			loadConfig(DuplicateOrderConfig);
-		then:
-			BeanCreationException e = thrown()
-			e.message.contains "@Order on WebSecurityConfigurers must be unique"
-			e.message.contains DuplicateOrderConfig.WebConfigurer1.class.name
-			e.message.contains DuplicateOrderConfig.WebConfigurer2.class.name
-	}
-
-
-	@EnableWebSecurity
-	static class DuplicateOrderConfig {
-		public AuthenticationManager authenticationManager() throws Exception {
-			return new AuthenticationManagerBuilder()
-				.inMemoryAuthentication()
-					.withUser("user").password("password").roles("USER").and()
-					.and()
-				.build();
-		}
-
-		@Configuration
-		public static class WebConfigurer1 extends WebSecurityConfigurerAdapter {
-
-			@Override
-			protected void configure(HttpSecurity http) throws Exception {
-				http
-					.antMatcher("/role1/**")
-					.authorizeRequests()
-						.anyRequest().hasRole("1");
-			}
-		}
-
-		@Configuration
-		public static class WebConfigurer2 extends WebSecurityConfigurerAdapter {
-			@Override
-			protected void configure(HttpSecurity http) throws Exception {
-				http
-					.antMatcher("/role2/**")
-					.authorizeRequests()
-						.anyRequest().hasRole("2");
-			}
-		}
-	}
-
-	def "Override privilegeEvaluator"() {
-		setup:
-			WebInvocationPrivilegeEvaluator privilegeEvaluator = Mock()
-			PrivilegeEvaluatorConfigurerAdapterConfig.PE = privilegeEvaluator
-		when:
-			loadConfig(PrivilegeEvaluatorConfigurerAdapterConfig)
-		then:
-			context.getBean(WebInvocationPrivilegeEvaluator) == privilegeEvaluator
-	}
-
-	@EnableWebSecurity
-	static class PrivilegeEvaluatorConfigurerAdapterConfig extends WebSecurityConfigurerAdapter {
-		static WebInvocationPrivilegeEvaluator PE
-
-		@Override
-		public void configure(WebSecurity web) throws Exception {
-			web
-				.privilegeEvaluator(PE)
-		}
-	}
-
-	def "Override webSecurityExpressionHandler"() {
-		setup:
-			SecurityExpressionHandler expressionHandler = Mock()
-			ExpressionParser parser = Mock()
-			WebSecurityExpressionHandlerConfig.EH = expressionHandler
-		when:
-			loadConfig(WebSecurityExpressionHandlerConfig)
-		then:
-			context.getBean(SecurityExpressionHandler) == expressionHandler
-			1 * expressionHandler.getExpressionParser() >> parser
-	}
-
-	@EnableWebSecurity
-	static class WebSecurityExpressionHandlerConfig extends WebSecurityConfigurerAdapter {
-		static SecurityExpressionHandler EH
-
-		@Override
-		public void configure(WebSecurity web) throws Exception {
-			web
-				.expressionHandler(EH)
-		}
-		@Override
-		protected void configure(HttpSecurity http) throws Exception {
-			http
-				.authorizeRequests()
-					.expressionHandler(EH)
-					.anyRequest().authenticated()
-		}
-	}
-
-	def "#138 webSecurityExpressionHandler defaults"() {
-		when:
-			loadConfig(WebSecurityExpressionHandlerDefaultsConfig)
-		then:
-			SecurityExpressionHandler wseh = context.getBean(SecurityExpressionHandler)
-			wseh instanceof DefaultWebSecurityExpressionHandler
-	}
-
-	@EnableWebSecurity
-	static class WebSecurityExpressionHandlerDefaultsConfig extends WebSecurityConfigurerAdapter {
-
-		@Override
-		protected void configure(HttpSecurity http) throws Exception {
-			http
-				.authorizeRequests()
-					.anyRequest().authenticated()
-		}
-	}
-
-	def "#138 WebInvocationPrivilegeEvaluator defaults"() {
-		when:
-			loadConfig(WebInvocationPrivilegeEvaluatorDefaultsConfig)
-		then:
-			WebInvocationPrivilegeEvaluator wipe = context.getBean(WebInvocationPrivilegeEvaluator)
-			wipe instanceof DefaultWebInvocationPrivilegeEvaluator
-			wipe.securityInterceptor != null
-	}
-
-	@EnableWebSecurity
-	static class WebInvocationPrivilegeEvaluatorDefaultsConfig extends WebSecurityConfigurerAdapter {
-
-		@Override
-		protected void configure(HttpSecurity http) throws Exception {
-			http
-				.authorizeRequests()
-					.anyRequest().authenticated()
-		}
-	}
-
-	def "SEC-2303: DefaultExpressionHandler has bean resolver set"() {
-		when:
-			loadConfig(DefaultExpressionHandlerSetsBeanResolverConfig)
-		then: "the exposed bean has a BeanResolver set"
-			ReflectionTestUtils.getField(context.getBean(SecurityExpressionHandler),"br")
-		when:
-			springSecurityFilterChain.doFilter(request, response, chain)
-		then: "we can use the BeanResolver with a grant"
-			noExceptionThrown()
-		when: "we can use the Beanresolver with a deny"
-			springSecurityFilterChain.doFilter(new MockHttpServletRequest(method:'POST'), response, chain)
-		then:
-			noExceptionThrown()
-	}
-
-	@EnableWebSecurity
-	static class DefaultExpressionHandlerSetsBeanResolverConfig extends WebSecurityConfigurerAdapter {
-
-		@Override
-		protected void configure(HttpSecurity http) throws Exception {
-			http
-				.authorizeRequests()
-					.anyRequest().access("request.method == 'GET' ? @b.grant() : @b.deny()")
-		}
-
-		@Bean
-		public MyBean b() {
-			new MyBean()
-		}
-
-		static class MyBean {
-			boolean deny() {
-				false
-			}
-
-			boolean grant() {
-				true
-			}
-		}
-	}
-
-	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
-	static class ParentConfig extends WebSecurityConfigurerAdapter {
-		@Autowired
-		public void configureGlobal(AuthenticationManagerBuilder auth) {
-			auth.inMemoryAuthentication()
-		}
-	}
-
-	@EnableWebSecurity
-	static class ChildConfig extends WebSecurityConfigurerAdapter { }
-
-	def "SEC-2773: delegatingApplicationListener is static method"() {
-		expect: 'delegatingApplicationListener to prevent premature instantiation of WebSecurityConfiguration'
-		Modifier.isStatic(WebSecurityConfiguration.metaClass.methods.find { it.name == 'delegatingApplicationListener'}.modifiers)
-	}
-}

+ 360 - 0
config/src/test/java/org/springframework/security/config/annotation/web/configuration/WebSecurityConfigurationTests.java

@@ -0,0 +1,360 @@
+/*
+ * Copyright 2002-2018 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 org.junit.Rule;
+import org.junit.Test;
+import org.springframework.beans.factory.BeanCreationException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+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.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.config.test.SpringTestRule;
+import org.springframework.security.config.users.AuthenticationTestConfiguration;
+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.test.web.servlet.MockMvc;
+import org.springframework.util.ClassUtils;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.catchThrowable;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+/**
+ * Tests for {@link WebSecurityConfiguration}.
+ *
+ * @author Rob Winch
+ * @author Joe Grandja
+ */
+public class WebSecurityConfigurationTests {
+	@Rule
+	public final SpringTestRule spring = new SpringTestRule();
+
+	@Autowired
+	private MockMvc mockMvc;
+
+	@Test
+	public void loadConfigWhenWebSecurityConfigurersHaveOrderThenFilterChainsOrdered() throws Exception {
+		this.spring.register(SortedWebSecurityConfigurerAdaptersConfig.class).autowire();
+
+		FilterChainProxy filterChainProxy = this.spring.getContext().getBean(FilterChainProxy.class);
+		List<SecurityFilterChain> filterChains = filterChainProxy.getFilterChains();
+		assertThat(filterChains).hasSize(6);
+
+		MockHttpServletRequest request = new MockHttpServletRequest("GET", "");
+
+		request.setServletPath("/ignore1");
+		assertThat(filterChains.get(0).matches(request)).isTrue();
+		assertThat(filterChains.get(0).getFilters()).isEmpty();
+
+		request.setServletPath("/ignore2");
+		assertThat(filterChains.get(1).matches(request)).isTrue();
+		assertThat(filterChains.get(1).getFilters()).isEmpty();
+
+		request.setServletPath("/role1/**");
+		assertThat(filterChains.get(2).matches(request)).isTrue();
+
+		request.setServletPath("/role2/**");
+		assertThat(filterChains.get(3).matches(request)).isTrue();
+
+		request.setServletPath("/role3/**");
+		assertThat(filterChains.get(4).matches(request)).isTrue();
+
+		request.setServletPath("/**");
+		assertThat(filterChains.get(5).matches(request)).isTrue();
+	}
+
+	@EnableWebSecurity
+	@Import(AuthenticationTestConfiguration.class)
+	static class SortedWebSecurityConfigurerAdaptersConfig {
+
+		@Configuration
+		@Order(1)
+		static class WebConfigurer1 extends WebSecurityConfigurerAdapter {
+			@Override
+			public void configure(WebSecurity web)	throws Exception {
+				web
+					.ignoring()
+						.antMatchers("/ignore1", "/ignore2");
+			}
+
+			@Override
+			protected void configure(HttpSecurity http) throws Exception {
+				http
+					.antMatcher("/role1/**")
+					.authorizeRequests()
+						.anyRequest().hasRole("1");
+			}
+		}
+
+		@Configuration
+		@Order(2)
+		static class WebConfigurer2 extends WebSecurityConfigurerAdapter {
+			@Override
+			protected void configure(HttpSecurity http) throws Exception {
+				http
+					.antMatcher("/role2/**")
+					.authorizeRequests()
+						.anyRequest().hasRole("2");
+			}
+		}
+
+		@Configuration
+		@Order(3)
+		static class WebConfigurer3 extends WebSecurityConfigurerAdapter {
+			@Override
+			protected void configure(HttpSecurity http) throws Exception {
+				http
+					.antMatcher("/role3/**")
+					.authorizeRequests()
+						.anyRequest().hasRole("3");
+			}
+		}
+
+		@Configuration
+		static class WebConfigurer4 extends WebSecurityConfigurerAdapter {
+
+			@Override
+			protected void configure(HttpSecurity http) throws Exception {
+				http
+					.authorizeRequests()
+						.anyRequest().hasRole("4");
+			}
+		}
+	}
+
+	@Test
+	public void loadConfigWhenWebSecurityConfigurersHaveSameOrderThenThrowBeanCreationException() throws Exception {
+		Throwable thrown = catchThrowable(() -> this.spring.register(DuplicateOrderConfig.class).autowire());
+
+		assertThat(thrown).isInstanceOf(BeanCreationException.class)
+			.hasMessageContaining("@Order on WebSecurityConfigurers must be unique")
+			.hasMessageContaining(DuplicateOrderConfig.WebConfigurer1.class.getName())
+			.hasMessageContaining(DuplicateOrderConfig.WebConfigurer2.class.getName());
+	}
+
+	@EnableWebSecurity
+	@Import(AuthenticationTestConfiguration.class)
+	static class DuplicateOrderConfig {
+
+		@Configuration
+		static class WebConfigurer1 extends WebSecurityConfigurerAdapter {
+			@Override
+			protected void configure(HttpSecurity http) throws Exception {
+				http
+					.antMatcher("/role1/**")
+						.authorizeRequests()
+							.anyRequest().hasRole("1");
+			}
+		}
+
+		@Configuration
+		static class WebConfigurer2 extends WebSecurityConfigurerAdapter {
+			@Override
+			protected void configure(HttpSecurity http) throws Exception {
+				http
+					.antMatcher("/role2/**")
+						.authorizeRequests()
+							.anyRequest().hasRole("2");
+			}
+		}
+	}
+
+	@Test
+	public void loadConfigWhenWebInvocationPrivilegeEvaluatorSetThenIsRegistered() throws Exception {
+		PrivilegeEvaluatorConfigurerAdapterConfig.PRIVILEGE_EVALUATOR = mock(WebInvocationPrivilegeEvaluator.class);
+
+		this.spring.register(PrivilegeEvaluatorConfigurerAdapterConfig.class).autowire();
+
+		assertThat(this.spring.getContext().getBean(WebInvocationPrivilegeEvaluator.class))
+			.isSameAs(PrivilegeEvaluatorConfigurerAdapterConfig.PRIVILEGE_EVALUATOR);
+	}
+
+	@EnableWebSecurity
+	static class PrivilegeEvaluatorConfigurerAdapterConfig extends WebSecurityConfigurerAdapter {
+		static WebInvocationPrivilegeEvaluator PRIVILEGE_EVALUATOR;
+
+		@Override
+		public void configure(WebSecurity web) throws Exception {
+			web.privilegeEvaluator(PRIVILEGE_EVALUATOR);
+		}
+	}
+
+	@Test
+	public void loadConfigWhenSecurityExpressionHandlerSetThenIsRegistered() throws Exception {
+		WebSecurityExpressionHandlerConfig.EXPRESSION_HANDLER = mock(SecurityExpressionHandler.class);
+		when(WebSecurityExpressionHandlerConfig.EXPRESSION_HANDLER.getExpressionParser()).thenReturn(mock(ExpressionParser.class));
+
+		this.spring.register(WebSecurityExpressionHandlerConfig.class).autowire();
+
+		assertThat(this.spring.getContext().getBean(SecurityExpressionHandler.class))
+			.isSameAs(WebSecurityExpressionHandlerConfig.EXPRESSION_HANDLER);
+	}
+
+	@EnableWebSecurity
+	static class WebSecurityExpressionHandlerConfig extends WebSecurityConfigurerAdapter {
+		static SecurityExpressionHandler EXPRESSION_HANDLER;
+
+		@Override
+		public void configure(WebSecurity web) throws Exception {
+			web.expressionHandler(EXPRESSION_HANDLER);
+		}
+
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			http
+				.authorizeRequests()
+					.anyRequest().authenticated()
+					.expressionHandler(EXPRESSION_HANDLER);
+		}
+	}
+
+	@Test
+	public void loadConfigWhenDefaultSecurityExpressionHandlerThenDefaultIsRegistered() throws Exception {
+		this.spring.register(WebSecurityExpressionHandlerDefaultsConfig.class).autowire();
+
+		assertThat(this.spring.getContext().getBean(SecurityExpressionHandler.class))
+			.isInstanceOf(DefaultWebSecurityExpressionHandler.class);
+	}
+
+	@EnableWebSecurity
+	static class WebSecurityExpressionHandlerDefaultsConfig extends WebSecurityConfigurerAdapter {
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			http
+				.authorizeRequests()
+					.anyRequest().authenticated();
+		}
+	}
+
+	@Test
+	public void loadConfigWhenDefaultWebInvocationPrivilegeEvaluatorThenDefaultIsRegistered() throws Exception {
+		this.spring.register(WebInvocationPrivilegeEvaluatorDefaultsConfig.class).autowire();
+
+		assertThat(this.spring.getContext().getBean(WebInvocationPrivilegeEvaluator.class))
+			.isInstanceOf(DefaultWebInvocationPrivilegeEvaluator.class);
+	}
+
+	@EnableWebSecurity
+	static class WebInvocationPrivilegeEvaluatorDefaultsConfig extends WebSecurityConfigurerAdapter {
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			http
+				.authorizeRequests()
+					.anyRequest().authenticated();
+		}
+	}
+
+	// SEC-2303
+	@Test
+	public void loadConfigWhenDefaultSecurityExpressionHandlerThenBeanResolverSet() throws Exception {
+		this.spring.register(DefaultExpressionHandlerSetsBeanResolverConfig.class).autowire();
+
+		this.mockMvc.perform(get("/")).andExpect(status().isOk());
+		this.mockMvc.perform(post("/")).andExpect(status().isForbidden());
+	}
+
+	@EnableWebSecurity
+	static class DefaultExpressionHandlerSetsBeanResolverConfig extends WebSecurityConfigurerAdapter {
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			http
+				.authorizeRequests()
+					.anyRequest().access("request.method == 'GET' ? @b.grant() : @b.deny()");
+		}
+
+		@RestController
+		public class HomeController {
+			@GetMapping("/")
+			public String home() {
+				return "home";
+			}
+		}
+
+		@Bean
+		public MyBean b() {
+			return new MyBean();
+		}
+
+		static class MyBean {
+			public boolean deny() {
+				return false;
+			}
+
+			public boolean grant() {
+				return true;
+			}
+		}
+	}
+
+	@Rule
+	public SpringTestRule child = new SpringTestRule();
+
+	// SEC-2461
+	@Test
+	public void loadConfigWhenMultipleWebSecurityConfigurationThenContextLoads() throws Exception {
+		this.spring.register(ParentConfig.class).autowire();
+
+		this.child.register(ChildConfig.class);
+		this.child.getContext().setParent(this.spring.getContext());
+		this.child.autowire();
+
+		assertThat(this.spring.getContext().getBean("springSecurityFilterChain")).isNotNull();
+		assertThat(this.child.getContext().getBean("springSecurityFilterChain")).isNotNull();
+
+		assertThat(this.spring.getContext().containsBean("springSecurityFilterChain")).isTrue();
+		assertThat(this.child.getContext().containsBean("springSecurityFilterChain")).isTrue();
+	}
+
+	@EnableWebSecurity
+	static class ParentConfig extends WebSecurityConfigurerAdapter {
+		@Autowired
+		public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
+			auth.inMemoryAuthentication();
+		}
+	}
+
+	@EnableWebSecurity
+	static class ChildConfig extends WebSecurityConfigurerAdapter {
+	}
+
+	// SEC-2773
+	@Test
+	public void getMethodDelegatingApplicationListenerWhenWebSecurityConfigurationThenIsStatic() throws Exception {
+		Method method = ClassUtils.getMethod(WebSecurityConfiguration.class, "delegatingApplicationListener", null);
+		assertThat(Modifier.isStatic(method.getModifiers())).isTrue();
+	}
+}