Prechádzať zdrojové kódy

Merge branch '5.8.x'

# Conflicts:
#	web/src/main/java/org/springframework/security/web/context/HttpSessionSecurityContextRepository.java
#	web/src/test/java/org/springframework/security/web/context/SecurityContextRepositoryTests.java
Steve Riesenberg 2 rokov pred
rodič
commit
bd43c1f28a

+ 26 - 1
config/src/test/java/org/springframework/security/config/http/MiscHttpConfigTests.java

@@ -75,6 +75,7 @@ import org.springframework.security.core.AuthenticationException;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.annotation.AuthenticationPrincipal;
 import org.springframework.security.core.authority.AuthorityUtils;
+import org.springframework.security.core.context.DeferredSecurityContext;
 import org.springframework.security.core.context.SecurityContext;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.core.context.SecurityContextHolderStrategy;
@@ -487,7 +488,8 @@ public class MiscHttpConfigTests {
 		this.spring.configLocations(xml("ExplicitSaveAndExplicitRepository")).autowire();
 		SecurityContextRepository repository = this.spring.getContext().getBean(SecurityContextRepository.class);
 		SecurityContext context = new SecurityContextImpl(new TestingAuthenticationToken("user", "password"));
-		given(repository.loadContext(any(HttpServletRequest.class))).willReturn(() -> context);
+		given(repository.loadDeferredContext(any(HttpServletRequest.class)))
+				.willReturn(new TestDeferredSecurityContext(context, false));
 		// @formatter:off
 		MvcResult result = this.mvc.perform(formLogin())
 				.andExpect(status().is3xxRedirection())
@@ -1037,4 +1039,27 @@ public class MiscHttpConfigTests {
 
 	}
 
+	static class TestDeferredSecurityContext implements DeferredSecurityContext {
+
+		private SecurityContext securityContext;
+
+		private boolean isGenerated;
+
+		TestDeferredSecurityContext(SecurityContext securityContext, boolean isGenerated) {
+			this.securityContext = securityContext;
+			this.isGenerated = isGenerated;
+		}
+
+		@Override
+		public SecurityContext get() {
+			return this.securityContext;
+		}
+
+		@Override
+		public boolean isGenerated() {
+			return this.isGenerated;
+		}
+
+	}
+
 }

+ 38 - 0
core/src/main/java/org/springframework/security/core/context/DeferredSecurityContext.java

@@ -0,0 +1,38 @@
+/*
+ * 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.core.context;
+
+import java.util.function.Supplier;
+
+/**
+ * An interface that allows delayed access to a {@link SecurityContext} that may be
+ * generated.
+ *
+ * @author Steve Riesenberg
+ * @since 5.8
+ */
+public interface DeferredSecurityContext extends Supplier<SecurityContext> {
+
+	/**
+	 * Returns true if {@link #get()} refers to a generated {@link SecurityContext} or
+	 * false if it already existed.
+	 * @return true if {@link #get()} refers to a generated {@link SecurityContext} or
+	 * false if it already existed
+	 */
+	boolean isGenerated();
+
+}

+ 111 - 0
web/src/main/java/org/springframework/security/web/context/DelegatingSecurityContextRepository.java

@@ -0,0 +1,111 @@
+/*
+ * 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.web.context;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.security.core.context.DeferredSecurityContext;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.util.Assert;
+
+/**
+ * @author Steve Riesenberg
+ * @author Josh Cummings
+ * @since 5.8
+ */
+public final class DelegatingSecurityContextRepository implements SecurityContextRepository {
+
+	private final List<SecurityContextRepository> delegates;
+
+	public DelegatingSecurityContextRepository(SecurityContextRepository... delegates) {
+		this(Arrays.asList(delegates));
+	}
+
+	public DelegatingSecurityContextRepository(List<SecurityContextRepository> delegates) {
+		Assert.notEmpty(delegates, "delegates cannot be empty");
+		this.delegates = delegates;
+	}
+
+	@Override
+	public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {
+		return loadContext(requestResponseHolder.getRequest()).get();
+	}
+
+	@Override
+	public DeferredSecurityContext loadDeferredContext(HttpServletRequest request) {
+		DeferredSecurityContext deferredSecurityContext = null;
+		for (SecurityContextRepository delegate : this.delegates) {
+			if (deferredSecurityContext == null) {
+				deferredSecurityContext = delegate.loadDeferredContext(request);
+			}
+			else {
+				DeferredSecurityContext next = delegate.loadDeferredContext(request);
+				deferredSecurityContext = new DelegatingDeferredSecurityContext(deferredSecurityContext, next);
+			}
+		}
+		return deferredSecurityContext;
+	}
+
+	@Override
+	public void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response) {
+		for (SecurityContextRepository delegate : this.delegates) {
+			delegate.saveContext(context, request, response);
+		}
+	}
+
+	@Override
+	public boolean containsContext(HttpServletRequest request) {
+		for (SecurityContextRepository delegate : this.delegates) {
+			if (delegate.containsContext(request)) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	static final class DelegatingDeferredSecurityContext implements DeferredSecurityContext {
+
+		private final DeferredSecurityContext previous;
+
+		private final DeferredSecurityContext next;
+
+		DelegatingDeferredSecurityContext(DeferredSecurityContext previous, DeferredSecurityContext next) {
+			this.previous = previous;
+			this.next = next;
+		}
+
+		@Override
+		public SecurityContext get() {
+			SecurityContext securityContext = this.previous.get();
+			if (!this.previous.isGenerated()) {
+				return securityContext;
+			}
+			return this.next.get();
+		}
+
+		@Override
+		public boolean isGenerated() {
+			return this.previous.isGenerated() && this.next.isGenerated();
+		}
+
+	}
+
+}

+ 2 - 1
web/src/main/java/org/springframework/security/web/context/HttpRequestResponseHolder.java

@@ -27,7 +27,8 @@ import jakarta.servlet.http.HttpServletResponse;
  *
  * @author Luke Taylor
  * @since 3.0
- * @deprecated Use {@link SecurityContextRepository#loadContext(HttpServletRequest)}
+ * @deprecated Use
+ * {@link SecurityContextRepository#loadDeferredContext(HttpServletRequest)}
  */
 @Deprecated
 public final class HttpRequestResponseHolder {

+ 9 - 0
web/src/main/java/org/springframework/security/web/context/HttpSessionSecurityContextRepository.java

@@ -16,6 +16,8 @@
 
 package org.springframework.security.web.context;
 
+import java.util.function.Supplier;
+
 import jakarta.servlet.AsyncContext;
 import jakarta.servlet.ServletRequest;
 import jakarta.servlet.ServletResponse;
@@ -32,6 +34,7 @@ import org.springframework.security.authentication.AuthenticationTrustResolver;
 import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.Transient;
+import org.springframework.security.core.context.DeferredSecurityContext;
 import org.springframework.security.core.context.SecurityContext;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.core.context.SecurityContextHolderStrategy;
@@ -135,6 +138,12 @@ public class HttpSessionSecurityContextRepository implements SecurityContextRepo
 		return context;
 	}
 
+	@Override
+	public DeferredSecurityContext loadDeferredContext(HttpServletRequest request) {
+		Supplier<SecurityContext> supplier = () -> readSecurityContextFromSession(request.getSession(false));
+		return new SupplierDeferredSecurityContext(supplier, this.securityContextHolderStrategy);
+	}
+
 	@Override
 	public void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response) {
 		SaveContextOnUpdateOrErrorResponseWrapper responseWrapper = WebUtils.getNativeResponse(response,

+ 5 - 8
web/src/main/java/org/springframework/security/web/context/RequestAttributeSecurityContextRepository.java

@@ -21,6 +21,7 @@ import java.util.function.Supplier;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 
+import org.springframework.security.core.context.DeferredSecurityContext;
 import org.springframework.security.core.context.SecurityContext;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.core.context.SecurityContextHolderStrategy;
@@ -76,17 +77,13 @@ public final class RequestAttributeSecurityContextRepository implements Security
 
 	@Override
 	public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {
-		return getContextOrEmpty(requestResponseHolder.getRequest());
+		return loadDeferredContext(requestResponseHolder.getRequest()).get();
 	}
 
 	@Override
-	public Supplier<SecurityContext> loadContext(HttpServletRequest request) {
-		return () -> getContextOrEmpty(request);
-	}
-
-	private SecurityContext getContextOrEmpty(HttpServletRequest request) {
-		SecurityContext context = getContext(request);
-		return (context != null) ? context : this.securityContextHolderStrategy.createEmptyContext();
+	public DeferredSecurityContext loadDeferredContext(HttpServletRequest request) {
+		Supplier<SecurityContext> supplier = () -> getContext(request);
+		return new SupplierDeferredSecurityContext(supplier, this.securityContextHolderStrategy);
 	}
 
 	private SecurityContext getContext(HttpServletRequest request) {

+ 2 - 2
web/src/main/java/org/springframework/security/web/context/SaveContextOnUpdateOrErrorResponseWrapper.java

@@ -42,8 +42,8 @@ import org.springframework.util.Assert;
  * @author Marten Algesten
  * @author Rob Winch
  * @since 3.0
- * @deprecated Use {@link SecurityContextRepository#loadContext(HttpServletRequest)}
- * instead.
+ * @deprecated Use
+ * {@link SecurityContextRepository#loadDeferredContext(HttpServletRequest)} instead.
  */
 @Deprecated
 public abstract class SaveContextOnUpdateOrErrorResponseWrapper extends OnCommittedResponseWrapper {

+ 1 - 1
web/src/main/java/org/springframework/security/web/context/SecurityContextHolderFilter.java

@@ -63,7 +63,7 @@ public class SecurityContextHolderFilter extends OncePerRequestFilter {
 	@Override
 	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
 			throws ServletException, IOException {
-		Supplier<SecurityContext> deferredContext = this.securityContextRepository.loadContext(request);
+		Supplier<SecurityContext> deferredContext = this.securityContextRepository.loadDeferredContext(request);
 		try {
 			this.securityContextHolderStrategy.setDeferredContext(deferredContext);
 			filterChain.doFilter(request, response);

+ 23 - 3
web/src/main/java/org/springframework/security/web/context/SecurityContextRepository.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2016 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 java.util.function.Supplier;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 
+import org.springframework.security.core.context.DeferredSecurityContext;
 import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.util.function.SingletonSupplier;
 
 /**
@@ -61,7 +63,7 @@ public interface SecurityContextRepository {
 	 * the context should be loaded.
 	 * @return The security context which should be used for the current request, never
 	 * null.
-	 * @deprecated Use {@link #loadContext(HttpServletRequest)} instead.
+	 * @deprecated Use {@link #loadDeferredContext(HttpServletRequest)} instead.
 	 */
 	@Deprecated
 	SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder);
@@ -75,9 +77,27 @@ public interface SecurityContextRepository {
 	 * @return a {@link Supplier} that returns the {@link SecurityContext} which cannot be
 	 * null.
 	 * @since 5.7
+	 * @deprecated Use
+	 * {@link SecurityContextRepository#loadDeferredContext(HttpServletRequest)} instead
 	 */
+	@Deprecated
 	default Supplier<SecurityContext> loadContext(HttpServletRequest request) {
-		return SingletonSupplier.of(() -> loadContext(new HttpRequestResponseHolder(request, null)));
+		return loadDeferredContext(request);
+	}
+
+	/**
+	 * Defers loading the {@link SecurityContext} using the {@link HttpServletRequest}
+	 * until it is needed by the application.
+	 * @param request the {@link HttpServletRequest} to load the {@link SecurityContext}
+	 * from
+	 * @return a {@link DeferredSecurityContext} that returns the {@link SecurityContext}
+	 * which cannot be null
+	 * @since 5.8
+	 */
+	default DeferredSecurityContext loadDeferredContext(HttpServletRequest request) {
+		Supplier<SecurityContext> supplier = () -> loadContext(new HttpRequestResponseHolder(request, null));
+		return new SupplierDeferredSecurityContext(SingletonSupplier.of(supplier),
+				SecurityContextHolder.getContextHolderStrategy());
 	}
 
 	/**

+ 77 - 0
web/src/main/java/org/springframework/security/web/context/SupplierDeferredSecurityContext.java

@@ -0,0 +1,77 @@
+/*
+ * 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.web.context;
+
+import java.util.function.Supplier;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.core.log.LogMessage;
+import org.springframework.security.core.context.DeferredSecurityContext;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolderStrategy;
+
+/**
+ * @author Steve Riesenberg
+ * @since 5.8
+ */
+final class SupplierDeferredSecurityContext implements DeferredSecurityContext {
+
+	private static final Log logger = LogFactory.getLog(SupplierDeferredSecurityContext.class);
+
+	private final Supplier<SecurityContext> supplier;
+
+	private final SecurityContextHolderStrategy strategy;
+
+	private SecurityContext securityContext;
+
+	private boolean missingContext;
+
+	SupplierDeferredSecurityContext(Supplier<SecurityContext> supplier, SecurityContextHolderStrategy strategy) {
+		this.supplier = supplier;
+		this.strategy = strategy;
+	}
+
+	@Override
+	public SecurityContext get() {
+		init();
+		return this.securityContext;
+	}
+
+	@Override
+	public boolean isGenerated() {
+		init();
+		return this.missingContext;
+	}
+
+	private void init() {
+		if (this.securityContext != null) {
+			return;
+		}
+
+		this.securityContext = this.supplier.get();
+		this.missingContext = (this.securityContext == null);
+		if (this.missingContext) {
+			this.securityContext = this.strategy.createEmptyContext();
+			if (logger.isTraceEnabled()) {
+				logger.trace(LogMessage.format("Created %s", this.securityContext));
+			}
+		}
+	}
+
+}

+ 144 - 0
web/src/test/java/org/springframework/security/web/context/DelegatingSecurityContextRepositoryTests.java

@@ -0,0 +1,144 @@
+/*
+ * 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.web.context;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
+
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpServletResponse;
+import org.springframework.security.authentication.TestingAuthenticationToken;
+import org.springframework.security.core.context.DeferredSecurityContext;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolderStrategy;
+import org.springframework.security.core.context.SecurityContextImpl;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+/**
+ * Tests for {@link DelegatingSecurityContextRepository}.
+ *
+ * @author Steve Riesenberg
+ * @since 5.8
+ */
+public class DelegatingSecurityContextRepositoryTests {
+
+	private MockHttpServletRequest request;
+
+	private MockHttpServletResponse response;
+
+	private SecurityContextHolderStrategy strategy;
+
+	private SecurityContext securityContext;
+
+	@BeforeEach
+	public void setUp() {
+		this.request = new MockHttpServletRequest();
+		this.response = new MockHttpServletResponse();
+		this.strategy = mock(SecurityContextHolderStrategy.class);
+		this.securityContext = mock(SecurityContext.class);
+	}
+
+	@ParameterizedTest
+	@CsvSource({ "0,false", "1,false", "2,false", "-1,true" })
+	public void loadDeferredContextWhenIsGeneratedThenReturnsSecurityContext(int expectedIndex, boolean isGenerated) {
+		SecurityContext actualSecurityContext = new SecurityContextImpl(
+				new TestingAuthenticationToken("user", "password"));
+		SecurityContext emptySecurityContext = new SecurityContextImpl();
+		given(this.strategy.createEmptyContext()).willReturn(emptySecurityContext);
+		List<SecurityContextRepository> delegates = new ArrayList<>();
+		for (int i = 0; i < 3; i++) {
+			SecurityContext context = (i == expectedIndex) ? actualSecurityContext : null;
+			SecurityContextRepository repository = mock(SecurityContextRepository.class);
+			SupplierDeferredSecurityContext supplier = new SupplierDeferredSecurityContext(() -> context,
+					this.strategy);
+			given(repository.loadDeferredContext(this.request)).willReturn(supplier);
+			delegates.add(repository);
+		}
+
+		DelegatingSecurityContextRepository repository = new DelegatingSecurityContextRepository(delegates);
+		DeferredSecurityContext deferredSecurityContext = repository.loadDeferredContext(this.request);
+		SecurityContext expectedSecurityContext = (isGenerated) ? emptySecurityContext : actualSecurityContext;
+		assertThat(deferredSecurityContext.get()).isEqualTo(expectedSecurityContext);
+		assertThat(deferredSecurityContext.isGenerated()).isEqualTo(isGenerated);
+
+		for (SecurityContextRepository delegate : delegates) {
+			verify(delegate).loadDeferredContext(this.request);
+			verifyNoMoreInteractions(delegate);
+		}
+	}
+
+	@Test
+	public void saveContextAlwaysCallsDelegates() {
+		List<SecurityContextRepository> delegates = new ArrayList<>();
+		for (int i = 0; i < 3; i++) {
+			SecurityContextRepository repository = mock(SecurityContextRepository.class);
+			delegates.add(repository);
+		}
+
+		DelegatingSecurityContextRepository repository = new DelegatingSecurityContextRepository(delegates);
+		repository.saveContext(this.securityContext, this.request, this.response);
+		for (SecurityContextRepository delegate : delegates) {
+			verify(delegate).saveContext(this.securityContext, this.request, this.response);
+			verifyNoMoreInteractions(delegate);
+		}
+	}
+
+	@Test
+	public void containsContextWhenAllDelegatesReturnFalseThenReturnsFalse() {
+		List<SecurityContextRepository> delegates = new ArrayList<>();
+		for (int i = 0; i < 3; i++) {
+			SecurityContextRepository repository = mock(SecurityContextRepository.class);
+			given(repository.containsContext(this.request)).willReturn(false);
+			delegates.add(repository);
+		}
+
+		DelegatingSecurityContextRepository repository = new DelegatingSecurityContextRepository(delegates);
+		assertThat(repository.containsContext(this.request)).isFalse();
+		for (SecurityContextRepository delegate : delegates) {
+			verify(delegate).containsContext(this.request);
+			verifyNoMoreInteractions(delegate);
+		}
+	}
+
+	@Test
+	public void containsContextWhenFirstDelegatesReturnTrueThenReturnsTrue() {
+		List<SecurityContextRepository> delegates = new ArrayList<>();
+		for (int i = 0; i < 3; i++) {
+			SecurityContextRepository repository = mock(SecurityContextRepository.class);
+			given(repository.containsContext(this.request)).willReturn(true);
+			delegates.add(repository);
+		}
+
+		DelegatingSecurityContextRepository repository = new DelegatingSecurityContextRepository(delegates);
+		assertThat(repository.containsContext(this.request)).isTrue();
+		verify(delegates.get(0)).containsContext(this.request);
+		verifyNoInteractions(delegates.get(1));
+		verifyNoInteractions(delegates.get(2));
+	}
+
+}

+ 4 - 2
web/src/test/java/org/springframework/security/web/context/SecurityContextHolderFilterTests.java

@@ -75,7 +75,8 @@ class SecurityContextHolderFilterTests {
 	void doFilterThenSetsAndClearsSecurityContextHolder() throws Exception {
 		Authentication authentication = TestAuthentication.authenticatedUser();
 		SecurityContext expectedContext = new SecurityContextImpl(authentication);
-		given(this.repository.loadContext(this.requestArg.capture())).willReturn(() -> expectedContext);
+		given(this.repository.loadDeferredContext(this.requestArg.capture()))
+				.willReturn(new SupplierDeferredSecurityContext(() -> expectedContext, this.strategy));
 		FilterChain filterChain = (request, response) -> assertThat(SecurityContextHolder.getContext())
 				.isEqualTo(expectedContext);
 
@@ -88,7 +89,8 @@ class SecurityContextHolderFilterTests {
 	void doFilterThenSetsAndClearsSecurityContextHolderStrategy() throws Exception {
 		Authentication authentication = TestAuthentication.authenticatedUser();
 		SecurityContext expectedContext = new SecurityContextImpl(authentication);
-		given(this.repository.loadContext(this.requestArg.capture())).willReturn(() -> expectedContext);
+		given(this.repository.loadDeferredContext(this.requestArg.capture()))
+				.willReturn(new SupplierDeferredSecurityContext(() -> expectedContext, this.strategy));
 		FilterChain filterChain = (request, response) -> {
 		};
 

+ 3 - 5
web/src/test/java/org/springframework/security/web/context/SecurityContextRepositoryTests.java

@@ -16,12 +16,10 @@
 
 package org.springframework.security.web.context;
 
-import java.util.function.Supplier;
-
 import jakarta.servlet.http.HttpServletRequest;
 import org.junit.jupiter.api.Test;
 
-import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.DeferredSecurityContext;
 import org.springframework.security.core.context.SecurityContextImpl;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -41,8 +39,8 @@ class SecurityContextRepositoryTests {
 	@Test
 	void loadContextHttpRequestResponseHolderWhenInvokeSupplierTwiceThenOnlyInvokesLoadContextOnce() {
 		given(this.repository.loadContext(any(HttpRequestResponseHolder.class))).willReturn(new SecurityContextImpl());
-		Supplier<SecurityContext> deferredContext = this.repository.loadContext(mock(HttpServletRequest.class));
-		verify(this.repository).loadContext(any(HttpServletRequest.class));
+		DeferredSecurityContext deferredContext = this.repository.loadDeferredContext(mock(HttpServletRequest.class));
+		verify(this.repository).loadDeferredContext(any(HttpServletRequest.class));
 		deferredContext.get();
 		verify(this.repository).loadContext(any(HttpRequestResponseHolder.class));
 		deferredContext.get();