1
0
mirror of synced 2026-08-05 17:57:15 +00:00

Add SecurityContextRepository.loadContext(HttpServletRequest)

This allows loading the SecurityContext lazily, without the need for the
response, and does not attempt to automatically save the request when
the response is comitted.

Closes gh-11028
This commit is contained in:
Rob Winch
2022-03-25 13:45:30 -05:00
parent 1e3106f3a2
commit e176d764ba
7 changed files with 57 additions and 15 deletions
@@ -16,6 +16,8 @@
package org.springframework.security.web.context;
import java.util.function.Supplier;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@@ -64,17 +66,18 @@ public final class RequestAttributeSecurityContextRepository implements Security
@Override
public boolean containsContext(HttpServletRequest request) {
return loadContext(request) != null;
return loadContext(request).get() != null;
}
@Override
public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {
SecurityContext context = loadContext(requestResponseHolder.getRequest());
SecurityContext context = loadContext(requestResponseHolder.getRequest()).get();
return (context != null) ? context : SecurityContextHolder.createEmptyContext();
}
private SecurityContext loadContext(HttpServletRequest request) {
return (SecurityContext) request.getAttribute(this.requestAttributeName);
@Override
public Supplier<SecurityContext> loadContext(HttpServletRequest request) {
return () -> (SecurityContext) request.getAttribute(this.requestAttributeName);
}
@Override
@@ -58,8 +58,7 @@ public class SecurityContextHolderFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
SecurityContext securityContext = this.securityContextRepository
.loadContext(new HttpRequestResponseHolder(request, response));
SecurityContext securityContext = this.securityContextRepository.loadContext(request).get();
try {
SecurityContextHolder.setContext(securityContext);
filterChain.doFilter(request, response);
@@ -16,6 +16,8 @@
package org.springframework.security.web.context;
import java.util.function.Supplier;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@@ -61,6 +63,20 @@ public interface SecurityContextRepository {
*/
SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder);
/**
* Obtains the security context for the supplied request. For an unauthenticated user,
* an empty context implementation should be returned. This method should not return
* null.
* @param request the {@link HttpServletRequest} to load the {@link SecurityContext}
* from
* @return a {@link Supplier} that returns the {@link SecurityContext} which cannot be
* null.
* @since 5.7
*/
default Supplier<SecurityContext> loadContext(HttpServletRequest request) {
return () -> loadContext(new HttpRequestResponseHolder(request, null));
}
/**
* Stores the security context on completion of a request.
* @param context the non-null context which was obtained from the holder.
@@ -63,6 +63,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
/**
* @author Luke Taylor
@@ -141,6 +142,33 @@ public class HttpSessionSecurityContextRepositoryTests {
assertThat(repo.loadContext(holder)).isEqualTo(SecurityContextHolder.createEmptyContext());
}
@Test
public void loadContextHttpServletRequestWhenNotSavedThenEmptyContextReturned() {
HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();
MockHttpServletRequest request = new MockHttpServletRequest();
assertThat(repo.loadContext(request).get()).isEqualTo(SecurityContextHolder.createEmptyContext());
}
@Test
public void loadContextHttpServletRequestWhenSavedThenSavedContextReturned() {
SecurityContextImpl expectedContext = new SecurityContextImpl(this.testToken);
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();
repo.saveContext(expectedContext, request, response);
assertThat(repo.loadContext(request).get()).isEqualTo(expectedContext);
}
@Test
public void loadContextHttpServletRequestWhenNotAccessedThenHttpSessionNotAccessed() {
HttpSession session = mock(HttpSession.class);
HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();
MockHttpServletRequest request = new MockHttpServletRequest();
request.setSession(session);
repo.loadContext(request);
verifyNoInteractions(session);
}
@Test
public void existingContextIsSuccessFullyLoadedFromSessionAndSavedBack() {
HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();
@@ -49,11 +49,8 @@ class SecurityContextHolderFilterTests {
@Mock
private HttpServletResponse response;
@Mock
private FilterChain chain;
@Captor
private ArgumentCaptor<HttpRequestResponseHolder> requestResponse;
private ArgumentCaptor<HttpServletRequest> requestArg;
private SecurityContextHolderFilter filter;
@@ -71,7 +68,7 @@ class SecurityContextHolderFilterTests {
void doFilterThenSetsAndClearsSecurityContextHolder() throws Exception {
Authentication authentication = TestAuthentication.authenticatedUser();
SecurityContext expectedContext = new SecurityContextImpl(authentication);
given(this.repository.loadContext(this.requestResponse.capture())).willReturn(expectedContext);
given(this.repository.loadContext(this.requestArg.capture())).willReturn(() -> expectedContext);
FilterChain filterChain = (request, response) -> assertThat(SecurityContextHolder.getContext())
.isEqualTo(expectedContext);