diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java b/config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java index e7397d9c04..21bc5d22a3 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java @@ -62,6 +62,7 @@ import org.springframework.security.web.PortMapper; import org.springframework.security.web.PortMapperImpl; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer; +import org.springframework.security.web.context.SecurityContextRepository; import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher; import org.springframework.security.web.session.HttpSessionEventPublisher; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; @@ -1248,6 +1249,39 @@ public final class HttpSecurity extends return getOrApply(new SecurityContextConfigurer<>()); } + /** + * Sets up management of the {@link SecurityContext} on the + * {@link SecurityContextHolder} between {@link HttpServletRequest}'s. This is + * automatically applied when using {@link WebSecurityConfigurerAdapter}. + * + * The following customization specifies the shared {@link SecurityContextRepository} + * + *
+	 * @Configuration
+	 * @EnableWebSecurity
+	 * public class SecurityContextSecurityConfig extends WebSecurityConfigurerAdapter {
+	 *
+	 * 	@Override
+	 * 	protected void configure(HttpSecurity http) throws Exception {
+	 * 		http
+	 * 			.securityContext(securityContext ->
+	 * 				securityContext
+	 * 					.securityContextRepository(SCR)
+	 * 			);
+	 * 	}
+	 * }
+	 * 
+ * + * @param securityContextCustomizer the {@link Customizer} to provide more options for + * the {@link SecurityContextConfigurer} + * @return the {@link HttpSecurity} for further customizations + * @throws Exception + */ + public HttpSecurity securityContext(Customizer> securityContextCustomizer) throws Exception { + securityContextCustomizer.customize(getOrApply(new SecurityContextConfigurer<>())); + return HttpSecurity.this; + } + /** * Integrates the {@link HttpServletRequest} methods with the values found on the * {@link SecurityContext}. This is automatically applied when using diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/SecurityContextConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/SecurityContextConfigurerTests.java index b9b0939344..5d0419d959 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/SecurityContextConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/SecurityContextConfigurerTests.java @@ -28,14 +28,22 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.test.SpringTestRule; import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.userdetails.PasswordEncodedUser; import org.springframework.security.web.context.HttpRequestResponseHolder; +import org.springframework.security.web.context.NullSecurityContextRepository; import org.springframework.security.web.context.SecurityContextPersistenceFilter; import org.springframework.security.web.context.SecurityContextRepository; import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter; import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import javax.servlet.http.HttpSession; + +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; +import static org.springframework.security.config.Customizer.withDefaults; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; /** @@ -151,4 +159,97 @@ public class SecurityContextConfigurerTests { // @formatter:on } } + + @Test + public void requestWhenSecurityContextWithDefaultsInLambdaThenSessionIsCreated() throws Exception { + this.spring.register(SecurityContextWithDefaultsInLambdaConfig.class).autowire(); + + MvcResult mvcResult = this.mvc.perform(formLogin()).andReturn(); + HttpSession session = mvcResult.getRequest().getSession(false); + assertThat(session).isNotNull(); + } + + @EnableWebSecurity + static class SecurityContextWithDefaultsInLambdaConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .formLogin(withDefaults()) + .securityContext(withDefaults()); + // @formatter:on + } + + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + // @formatter:off + auth + .inMemoryAuthentication() + .withUser(PasswordEncodedUser.user()); + // @formatter:on + } + } + + @Test + public void requestWhenSecurityContextDisabledInLambdaThenContextNotSavedInSession() throws Exception { + this.spring.register(SecurityContextDisabledInLambdaConfig.class).autowire(); + + MvcResult mvcResult = this.mvc.perform(formLogin()).andReturn(); + HttpSession session = mvcResult.getRequest().getSession(false); + assertThat(session).isNull(); + } + + @EnableWebSecurity + static class SecurityContextDisabledInLambdaConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .formLogin(withDefaults()) + .securityContext(AbstractHttpConfigurer::disable); + // @formatter:on + } + + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + // @formatter:off + auth + .inMemoryAuthentication() + .withUser(PasswordEncodedUser.user()); + // @formatter:on + } + } + + @Test + public void requestWhenNullSecurityContextRepositoryInLambdaThenContextNotSavedInSession() throws Exception { + this.spring.register(NullSecurityContextRepositoryInLambdaConfig.class).autowire(); + + MvcResult mvcResult = this.mvc.perform(formLogin()).andReturn(); + HttpSession session = mvcResult.getRequest().getSession(false); + assertThat(session).isNull(); + } + + @EnableWebSecurity + static class NullSecurityContextRepositoryInLambdaConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .formLogin(withDefaults()) + .securityContext(securityContext -> + securityContext + .securityContextRepository(new NullSecurityContextRepository()) + ); + // @formatter:on + } + + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + // @formatter:off + auth + .inMemoryAuthentication() + .withUser(PasswordEncodedUser.user()); + // @formatter:on + } + } }