浏览代码

Merge branch '5.8.x' into 6.0.x

Closes gh-13127
Josh Cummings 2 年之前
父节点
当前提交
46ad9c122e

+ 8 - 1
config/src/main/java/org/springframework/security/config/annotation/web/configurers/RememberMeConfigurer.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2022 the original author or authors.
+ * Copyright 2002-2023 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.
@@ -35,6 +35,7 @@ import org.springframework.security.web.authentication.rememberme.PersistentToke
 import org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter;
 import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;
 import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
+import org.springframework.security.web.context.SecurityContextRepository;
 import org.springframework.util.Assert;
 
 /**
@@ -288,6 +289,12 @@ public final class RememberMeConfigurer<H extends HttpSecurityBuilder<H>>
 		if (this.authenticationSuccessHandler != null) {
 			rememberMeFilter.setAuthenticationSuccessHandler(this.authenticationSuccessHandler);
 		}
+		SecurityContextConfigurer<?> securityContextConfigurer = http.getConfigurer(SecurityContextConfigurer.class);
+		if (securityContextConfigurer != null && securityContextConfigurer.isRequireExplicitSave()) {
+			SecurityContextRepository securityContextRepository = securityContextConfigurer
+					.getSecurityContextRepository();
+			rememberMeFilter.setSecurityContextRepository(securityContextRepository);
+		}
 		rememberMeFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
 		rememberMeFilter = postProcess(rememberMeFilter);
 		http.addFilter(rememberMeFilter);

+ 77 - 1
config/src/test/java/org/springframework/security/config/annotation/web/configurers/RememberMeConfigurerTests.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2022 the original author or authors.
+ * Copyright 2002-2023 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.
@@ -19,6 +19,8 @@ package org.springframework.security.config.annotation.web.configurers;
 import java.util.Collections;
 
 import jakarta.servlet.http.Cookie;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
 import jakarta.servlet.http.HttpSession;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
@@ -38,6 +40,7 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
 import org.springframework.security.config.test.SpringTestContext;
 import org.springframework.security.config.test.SpringTestContextExtension;
+import org.springframework.security.core.context.SecurityContext;
 import org.springframework.security.core.context.SecurityContextHolderStrategy;
 import org.springframework.security.core.userdetails.PasswordEncodedUser;
 import org.springframework.security.core.userdetails.User;
@@ -48,6 +51,9 @@ import org.springframework.security.web.SecurityFilterChain;
 import org.springframework.security.web.authentication.RememberMeServices;
 import org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter;
 import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;
+import org.springframework.security.web.context.HttpRequestResponseHolder;
+import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
+import org.springframework.security.web.context.SecurityContextRepository;
 import org.springframework.test.web.servlet.MockMvc;
 import org.springframework.test.web.servlet.MvcResult;
 import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
@@ -59,6 +65,7 @@ import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.BDDMockito.given;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.springframework.security.config.Customizer.withDefaults;
@@ -294,6 +301,24 @@ public class RememberMeConfigurerTests {
 		this.mvc.perform(requestWithRememberme).andExpect(remembermeAuthentication);
 	}
 
+	// gh-13104
+	@Test
+	public void getWhenCustomSecurityContextRepositoryThenUses() throws Exception {
+		this.spring.register(SecurityContextRepositoryConfig.class).autowire();
+		SecurityContextRepository repository = this.spring.getContext().getBean(SecurityContextRepository.class);
+		MvcResult mvcResult = this.mvc.perform(post("/login").with(csrf()).param("username", "user")
+				.param("password", "password").param("remember-me", "true")).andReturn();
+		Cookie rememberMeCookie = mvcResult.getResponse().getCookie("remember-me");
+		reset(repository);
+		// @formatter:off
+		MockHttpServletRequestBuilder request = get("/abc").cookie(rememberMeCookie);
+		SecurityMockMvcResultMatchers.AuthenticatedMatcher remembermeAuthentication = authenticated()
+				.withAuthentication((auth) -> assertThat(auth).isInstanceOf(RememberMeAuthenticationToken.class));
+		// @formatter:on
+		this.mvc.perform(request).andExpect(remembermeAuthentication);
+		verify(repository).saveContext(any(), any(), any());
+	}
+
 	@Configuration
 	@EnableWebSecurity
 	static class NullUserDetailsConfig {
@@ -577,4 +602,55 @@ public class RememberMeConfigurerTests {
 
 	}
 
+	@Configuration
+	@EnableWebSecurity
+	static class SecurityContextRepositoryConfig {
+
+		private SecurityContextRepository repository = spy(new SpySecurityContextRepository());
+
+		@Bean
+		SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())
+				.securityContext((context) -> context.securityContextRepository(this.repository))
+				.formLogin(withDefaults())
+				.rememberMe(withDefaults());
+			return http.build();
+			// @formatter:on
+		}
+
+		@Bean
+		SecurityContextRepository securityContextRepository() {
+			return this.repository;
+		}
+
+		@Bean
+		UserDetailsService userDetailsService() {
+			return new InMemoryUserDetailsManager(PasswordEncodedUser.user());
+		}
+
+		private static class SpySecurityContextRepository implements SecurityContextRepository {
+
+			SecurityContextRepository delegate = new HttpSessionSecurityContextRepository();
+
+			@Override
+			public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {
+				return this.delegate.loadContext(requestResponseHolder);
+			}
+
+			@Override
+			public void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response) {
+				this.delegate.saveContext(context, request, response);
+			}
+
+			@Override
+			public boolean containsContext(HttpServletRequest request) {
+				return this.delegate.containsContext(request);
+			}
+
+		}
+
+	}
+
 }