From 2c09a300b674f9e05ccb6f586a424afb603e9365 Mon Sep 17 00:00:00 2001 From: Josh Cummings Date: Thu, 26 May 2022 14:19:06 -0600 Subject: [PATCH] Add SecurityContextHolderStrategy Java Configuration for Defaults Issue gh-11061 --- .../WebMvcSecurityConfiguration.java | 11 +++- ...bstractAuthenticationFilterConfigurer.java | 3 +- .../configurers/AbstractHttpConfigurer.java | 22 +++++++- .../web/configurers/AnonymousConfigurer.java | 1 + .../AuthorizeHttpRequestsConfigurer.java | 1 + .../ExceptionHandlingConfigurer.java | 3 +- .../web/configurers/HttpBasicConfigurer.java | 3 +- .../web/configurers/LogoutConfigurer.java | 1 + .../SecurityContextConfigurer.java | 4 +- .../SessionManagementConfigurer.java | 4 +- .../MockSecurityContextHolderStrategy.java | 50 +++++++++++++++++ ...ontextChangedListenerArgumentMatchers.java | 54 +++++++++++++++++++ .../SecurityContextChangedListenerConfig.java | 46 ++++++++++++++++ .../configurers/AnonymousConfigurerTests.java | 17 +++++- .../ExceptionHandlingConfigurerTests.java | 18 ++++++- .../configurers/FormLoginConfigurerTests.java | 26 ++++++++- .../configurers/HttpBasicConfigurerTests.java | 17 +++++- 17 files changed, 269 insertions(+), 12 deletions(-) create mode 100644 config/src/test/java/org/springframework/security/config/MockSecurityContextHolderStrategy.java create mode 100644 config/src/test/java/org/springframework/security/config/annotation/SecurityContextChangedListenerArgumentMatchers.java create mode 100644 config/src/test/java/org/springframework/security/config/annotation/SecurityContextChangedListenerConfig.java diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configuration/WebMvcSecurityConfiguration.java b/config/src/main/java/org/springframework/security/config/annotation/web/configuration/WebMvcSecurityConfiguration.java index db63fcf82b..5bb70d3aeb 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configuration/WebMvcSecurityConfiguration.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configuration/WebMvcSecurityConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2022 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. @@ -24,6 +24,8 @@ import org.springframework.context.ApplicationContextAware; import org.springframework.context.annotation.Bean; import org.springframework.context.expression.BeanFactoryResolver; import org.springframework.expression.BeanResolver; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.context.SecurityContextHolderStrategy; import org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver; import org.springframework.security.web.method.annotation.CsrfTokenArgumentResolver; import org.springframework.security.web.method.annotation.CurrentSecurityContextArgumentResolver; @@ -50,11 +52,15 @@ class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContex private BeanResolver beanResolver; + private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder + .getContextHolderStrategy(); + @Override @SuppressWarnings("deprecation") public void addArgumentResolvers(List argumentResolvers) { AuthenticationPrincipalArgumentResolver authenticationPrincipalResolver = new AuthenticationPrincipalArgumentResolver(); authenticationPrincipalResolver.setBeanResolver(this.beanResolver); + authenticationPrincipalResolver.setSecurityContextHolderStrategy(this.securityContextHolderStrategy); argumentResolvers.add(authenticationPrincipalResolver); argumentResolvers .add(new org.springframework.security.web.bind.support.AuthenticationPrincipalArgumentResolver()); @@ -72,6 +78,9 @@ class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContex @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.beanResolver = new BeanFactoryResolver(applicationContext.getAutowireCapableBeanFactory()); + if (applicationContext.getBeanNamesForType(SecurityContextHolderStrategy.class).length == 1) { + this.securityContextHolderStrategy = applicationContext.getBean(SecurityContextHolderStrategy.class); + } } } diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AbstractAuthenticationFilterConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AbstractAuthenticationFilterConfigurer.java index 441c0a8494..44a8e84021 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AbstractAuthenticationFilterConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AbstractAuthenticationFilterConfigurer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2022 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. @@ -299,6 +299,7 @@ public abstract class AbstractAuthenticationFilterConfigurer, B extends HttpSecurityBuilder> extends SecurityConfigurerAdapter { + private SecurityContextHolderStrategy securityContextHolderStrategy; + /** * Disables the {@link AbstractHttpConfigurer} by removing it. After doing so a fresh * version of the configuration can be applied. @@ -49,4 +54,19 @@ public abstract class AbstractHttpConfigurer> } if (this.authenticationFilter == null) { this.authenticationFilter = new AnonymousAuthenticationFilter(getKey(), this.principal, this.authorities); + this.authenticationFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy()); } this.authenticationProvider = postProcess(this.authenticationProvider); http.authenticationProvider(this.authenticationProvider); diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurer.java index f06f4bde98..063b1fcda6 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurer.java @@ -86,6 +86,7 @@ public final class AuthorizeHttpRequestsConfigurer> getRequestCache(http)); AccessDeniedHandler deniedHandler = getAccessDeniedHandler(http); exceptionTranslationFilter.setAccessDeniedHandler(deniedHandler); + exceptionTranslationFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy()); exceptionTranslationFilter = postProcess(exceptionTranslationFilter); http.addFilter(exceptionTranslationFilter); } diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/HttpBasicConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/HttpBasicConfigurer.java index e45bb31a06..2e5df36fee 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/HttpBasicConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/HttpBasicConfigurer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2022 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. @@ -199,6 +199,7 @@ public final class HttpBasicConfigurer> if (rememberMeServices != null) { basicAuthenticationFilter.setRememberMeServices(rememberMeServices); } + basicAuthenticationFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy()); basicAuthenticationFilter = postProcess(basicAuthenticationFilter); http.addFilter(basicAuthenticationFilter); } diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/LogoutConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/LogoutConfigurer.java index be65179302..f862fd3cc6 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/LogoutConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/LogoutConfigurer.java @@ -329,6 +329,7 @@ public final class LogoutConfigurer> this.logoutHandlers.add(postProcess(new LogoutSuccessEventPublishingLogoutHandler())); LogoutHandler[] handlers = this.logoutHandlers.toArray(new LogoutHandler[0]); LogoutFilter result = new LogoutFilter(getLogoutSuccessHandler(), handlers); + result.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy()); result.setLogoutRequestMatcher(getLogoutRequestMatcher(http)); result = postProcess(result); return result; diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/SecurityContextConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/SecurityContextConfigurer.java index 7d9f5553dd..026c429a81 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/SecurityContextConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/SecurityContextConfigurer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2022 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. @@ -108,11 +108,13 @@ public final class SecurityContextConfigurer> if (this.requireExplicitSave) { SecurityContextHolderFilter securityContextHolderFilter = postProcess( new SecurityContextHolderFilter(securityContextRepository)); + securityContextHolderFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy()); http.addFilter(securityContextHolderFilter); } else { SecurityContextPersistenceFilter securityContextFilter = new SecurityContextPersistenceFilter( securityContextRepository); + securityContextFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy()); SessionManagementConfigurer sessionManagement = http.getConfigurer(SessionManagementConfigurer.class); SessionCreationPolicy sessionCreationPolicy = (sessionManagement != null) ? sessionManagement.getSessionCreationPolicy() : null; diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/SessionManagementConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/SessionManagementConfigurer.java index 504a396738..34d9131e40 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/SessionManagementConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/SessionManagementConfigurer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2022 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. @@ -370,6 +370,7 @@ public final class SessionManagementConfigurer> if (trustResolver != null) { sessionManagementFilter.setTrustResolver(trustResolver); } + sessionManagementFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy()); sessionManagementFilter = postProcess(sessionManagementFilter); http.addFilter(sessionManagementFilter); if (isConcurrentSessionControlEnabled()) { @@ -500,7 +501,6 @@ public final class SessionManagementConfigurer> concurrentSessionControlStrategy.setMaximumSessions(this.maximumSessions); concurrentSessionControlStrategy.setExceptionIfMaximumExceeded(this.maxSessionsPreventsLogin); concurrentSessionControlStrategy = postProcess(concurrentSessionControlStrategy); - RegisterSessionAuthenticationStrategy registerSessionStrategy = new RegisterSessionAuthenticationStrategy( sessionRegistry); registerSessionStrategy = postProcess(registerSessionStrategy); diff --git a/config/src/test/java/org/springframework/security/config/MockSecurityContextHolderStrategy.java b/config/src/test/java/org/springframework/security/config/MockSecurityContextHolderStrategy.java new file mode 100644 index 0000000000..699197694b --- /dev/null +++ b/config/src/test/java/org/springframework/security/config/MockSecurityContextHolderStrategy.java @@ -0,0 +1,50 @@ +/* + * Copyright 2002-2022 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 + * + * https://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; + +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolderStrategy; +import org.springframework.security.core.context.SecurityContextImpl; + +public class MockSecurityContextHolderStrategy implements SecurityContextHolderStrategy { + + private SecurityContext context; + + @Override + public void clearContext() { + this.context = null; + } + + @Override + public SecurityContext getContext() { + if (this.context == null) { + this.context = createEmptyContext(); + } + return this.context; + } + + @Override + public void setContext(SecurityContext context) { + this.context = context; + } + + @Override + public SecurityContext createEmptyContext() { + return new SecurityContextImpl(); + } + +} diff --git a/config/src/test/java/org/springframework/security/config/annotation/SecurityContextChangedListenerArgumentMatchers.java b/config/src/test/java/org/springframework/security/config/annotation/SecurityContextChangedListenerArgumentMatchers.java new file mode 100644 index 0000000000..e38ea96d6d --- /dev/null +++ b/config/src/test/java/org/springframework/security/config/annotation/SecurityContextChangedListenerArgumentMatchers.java @@ -0,0 +1,54 @@ +/* + * Copyright 2002-2022 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 + * + * https://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; + +import org.mockito.ArgumentMatcher; + +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextChangedEvent; + +import static org.mockito.ArgumentMatchers.argThat; + +public final class SecurityContextChangedListenerArgumentMatchers { + + public static SecurityContextChangedEvent setAuthentication(Class authenticationClass) { + return argThat(new ArgumentMatcher() { + public boolean matches(SecurityContextChangedEvent event) { + Authentication previous = authentication(event.getOldContext()); + Authentication next = authentication(event.getNewContext()); + return previous == null && next != null && authenticationClass.isAssignableFrom(next.getClass()); + } + + public String toString() { + return "authentication set to " + authenticationClass; + } + }); + } + + private static Authentication authentication(SecurityContext context) { + if (context == null) { + return null; + } + return context.getAuthentication(); + } + + private SecurityContextChangedListenerArgumentMatchers() { + + } + +} diff --git a/config/src/test/java/org/springframework/security/config/annotation/SecurityContextChangedListenerConfig.java b/config/src/test/java/org/springframework/security/config/annotation/SecurityContextChangedListenerConfig.java new file mode 100644 index 0000000000..d1d534ff2f --- /dev/null +++ b/config/src/test/java/org/springframework/security/config/annotation/SecurityContextChangedListenerConfig.java @@ -0,0 +1,46 @@ +/* + * Copyright 2002-2022 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 + * + * https://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; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.MockSecurityContextHolderStrategy; +import org.springframework.security.core.context.ListeningSecurityContextHolderStrategy; +import org.springframework.security.core.context.SecurityContextChangedListener; +import org.springframework.security.core.context.SecurityContextHolderStrategy; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; + +@Configuration +public class SecurityContextChangedListenerConfig { + + private SecurityContextHolderStrategy strategy = new MockSecurityContextHolderStrategy(); + + private SecurityContextChangedListener listener = mock(SecurityContextChangedListener.class); + + @Bean + SecurityContextHolderStrategy securityContextHolderStrategy() { + return spy(new ListeningSecurityContextHolderStrategy(this.strategy, this.listener)); + } + + @Bean + SecurityContextChangedListener securityContextChangedListener() { + return this.listener; + } + +} diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/AnonymousConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/AnonymousConfigurerTests.java index b1e81663db..9c396200b4 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/AnonymousConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/AnonymousConfigurerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2022 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. @@ -20,6 +20,8 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AnonymousAuthenticationToken; +import org.springframework.security.config.annotation.SecurityContextChangedListenerConfig; 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.configuration.EnableWebSecurity; @@ -27,13 +29,16 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur import org.springframework.security.config.test.SpringTestContext; import org.springframework.security.config.test.SpringTestContextExtension; import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.security.core.context.SecurityContextChangedListener; import org.springframework.security.core.userdetails.PasswordEncodedUser; import org.springframework.test.web.servlet.MockMvc; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import static org.mockito.Mockito.verify; import static org.springframework.security.config.Customizer.withDefaults; +import static org.springframework.security.config.annotation.SecurityContextChangedListenerArgumentMatchers.setAuthentication; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -62,6 +67,16 @@ public class AnonymousConfigurerTests { this.mockMvc.perform(get("/")).andExpect(content().string("principal")); } + @Test + public void requestWhenCustomSecurityContextHolderStrategyThenUses() throws Exception { + this.spring.register(AnonymousPrincipalInLambdaConfig.class, SecurityContextChangedListenerConfig.class, + PrincipalController.class).autowire(); + this.mockMvc.perform(get("/")).andExpect(content().string("principal")); + SecurityContextChangedListener listener = this.spring.getContext() + .getBean(SecurityContextChangedListener.class); + verify(listener).securityContextChanged(setAuthentication(AnonymousAuthenticationToken.class)); + } + @Test public void requestWhenAnonymousDisabledInLambdaThenRespondsWithForbidden() throws Exception { this.spring.register(AnonymousDisabledInLambdaConfig.class, PrincipalController.class).autowire(); diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/ExceptionHandlingConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/ExceptionHandlingConfigurerTests.java index 06d66a0f89..4c3550c4e2 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/ExceptionHandlingConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/ExceptionHandlingConfigurerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2022 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. @@ -26,7 +26,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; +import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.config.annotation.ObjectPostProcessor; +import org.springframework.security.config.annotation.SecurityContextChangedListenerConfig; 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.configuration.EnableWebSecurity; @@ -34,6 +36,8 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur import org.springframework.security.config.test.SpringTestContext; import org.springframework.security.config.test.SpringTestContextExtension; import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.context.SecurityContextChangedListener; +import org.springframework.security.core.context.SecurityContextHolderStrategy; import org.springframework.security.core.userdetails.User; import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.web.AuthenticationEntryPoint; @@ -47,6 +51,7 @@ import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static org.springframework.security.config.annotation.SecurityContextChangedListenerArgumentMatchers.setAuthentication; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -192,6 +197,17 @@ public class ExceptionHandlingConfigurerTests { .resolveMediaTypes(any(NativeWebRequest.class)); } + @Test + public void getWhenCustomSecurityContextHolderStrategyThenUsed() throws Exception { + this.spring.register(SecurityContextChangedListenerConfig.class, DefaultSecurityConfig.class).autowire(); + this.mvc.perform(get("/")); + SecurityContextHolderStrategy strategy = this.spring.getContext().getBean(SecurityContextHolderStrategy.class); + verify(strategy, atLeastOnce()).getContext(); + SecurityContextChangedListener listener = this.spring.getContext() + .getBean(SecurityContextChangedListener.class); + verify(listener).securityContextChanged(setAuthentication(AnonymousAuthenticationToken.class)); + } + @Test public void getWhenUsingDefaultsAndUnauthenticatedThenRedirectsToLogin() throws Exception { this.spring.register(DefaultHttpConfig.class).autowire(); diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurerTests.java index d6683b83c7..e8c4d12cdf 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2022 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. @@ -21,7 +21,9 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.config.annotation.ObjectPostProcessor; +import org.springframework.security.config.annotation.SecurityContextChangedListenerConfig; 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; @@ -30,6 +32,8 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur import org.springframework.security.config.test.SpringTestContext; import org.springframework.security.config.test.SpringTestContextExtension; import org.springframework.security.config.users.AuthenticationTestConfiguration; +import org.springframework.security.core.context.SecurityContextChangedListener; +import org.springframework.security.core.context.SecurityContextHolderStrategy; import org.springframework.security.core.userdetails.PasswordEncodedUser; import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders; import org.springframework.security.web.PortMapper; @@ -42,10 +46,12 @@ import org.springframework.test.web.servlet.MockMvc; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.springframework.security.config.Customizer.withDefaults; +import static org.springframework.security.config.annotation.SecurityContextChangedListenerArgumentMatchers.setAuthentication; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.logout; import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated; @@ -97,6 +103,24 @@ public class FormLoginConfigurerTests { // @formatter:on } + @Test + public void formLoginWhenSecurityContextHolderStrategyThenUses() throws Exception { + this.spring.register(FormLoginConfig.class, SecurityContextChangedListenerConfig.class).autowire(); + // @formatter:off + SecurityMockMvcRequestBuilders.FormLoginRequestBuilder loginRequest = formLogin() + .user("username", "user") + .password("password", "password"); + this.mockMvc.perform(loginRequest) + .andExpect(status().isFound()) + .andExpect(redirectedUrl("/")); + // @formatter:on + SecurityContextHolderStrategy strategy = this.spring.getContext().getBean(SecurityContextHolderStrategy.class); + verify(strategy, atLeastOnce()).getContext(); + SecurityContextChangedListener listener = this.spring.getContext() + .getBean(SecurityContextChangedListener.class); + verify(listener).securityContextChanged(setAuthentication(UsernamePasswordAuthenticationToken.class)); + } + @Test public void loginWhenFormLoginConfiguredThenHasDefaultFailureUrl() throws Exception { this.spring.register(FormLoginConfig.class).autowire(); diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/HttpBasicConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/HttpBasicConfigurerTests.java index 99594611dc..38b1821e6f 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/HttpBasicConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/HttpBasicConfigurerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2022 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. @@ -25,8 +25,10 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.ObjectPostProcessor; +import org.springframework.security.config.annotation.SecurityContextChangedListenerConfig; 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.configuration.EnableWebSecurity; @@ -35,6 +37,7 @@ import org.springframework.security.config.test.SpringTestContext; import org.springframework.security.config.test.SpringTestContextExtension; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.security.core.context.SecurityContextChangedListener; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; @@ -53,6 +56,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.springframework.security.config.Customizer.withDefaults; +import static org.springframework.security.config.annotation.SecurityContextChangedListenerArgumentMatchers.setAuthentication; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; @@ -133,6 +137,17 @@ public class HttpBasicConfigurerTests { .andExpect(content().string("user")); } + @Test + public void httpBasicWhenCustomSecurityContextHolderStrategyThenUses() throws Exception { + this.spring.register(HttpBasic.class, Users.class, Home.class, SecurityContextChangedListenerConfig.class) + .autowire(); + this.mvc.perform(get("/").with(httpBasic("user", "password"))).andExpect(status().isOk()) + .andExpect(content().string("user")); + SecurityContextChangedListener listener = this.spring.getContext() + .getBean(SecurityContextChangedListener.class); + verify(listener).securityContextChanged(setAuthentication(UsernamePasswordAuthenticationToken.class)); + } + @EnableWebSecurity static class ObjectPostProcessorConfig extends WebSecurityConfigurerAdapter {