Browse Source

Use SecurityContextHolderStrategy for Concurrency Filter

Issue gh-11060
Issue gh-11061
Josh Cummings 3 years ago
parent
commit
05b788d1ac

+ 1 - 0
config/src/main/java/org/springframework/security/config/annotation/web/configurers/SessionManagementConfigurer.java

@@ -400,6 +400,7 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
 				concurrentSessionFilter.setLogoutHandlers(logoutHandlers);
 			}
 		}
+		concurrentSessionFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
 		return concurrentSessionFilter;
 	}
 

+ 17 - 2
web/src/main/java/org/springframework/security/web/session/ConcurrentSessionFilter.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.
@@ -30,6 +30,7 @@ import jakarta.servlet.http.HttpSession;
 import org.springframework.core.log.LogMessage;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.context.SecurityContextHolderStrategy;
 import org.springframework.security.core.session.SessionInformation;
 import org.springframework.security.core.session.SessionRegistry;
 import org.springframework.security.web.RedirectStrategy;
@@ -67,6 +68,9 @@ import org.springframework.web.filter.GenericFilterBean;
  */
 public class ConcurrentSessionFilter extends GenericFilterBean {
 
+	private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
+			.getContextHolderStrategy();
+
 	private final SessionRegistry sessionRegistry;
 
 	private String expiredUrl;
@@ -162,11 +166,22 @@ public class ConcurrentSessionFilter extends GenericFilterBean {
 	}
 
 	private void doLogout(HttpServletRequest request, HttpServletResponse response) {
-		Authentication auth = SecurityContextHolder.getContext().getAuthentication();
+		Authentication auth = this.securityContextHolderStrategy.getContext().getAuthentication();
 
 		this.handlers.logout(request, response, auth);
 	}
 
+	/**
+	 * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use
+	 * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.
+	 *
+	 * @since 5.8
+	 */
+	public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
+		Assert.notNull(securityContextHolderStrategy, "securityContextHolderStrategy cannot be null");
+		this.securityContextHolderStrategy = securityContextHolderStrategy;
+	}
+
 	public void setLogoutHandlers(LogoutHandler[] handlers) {
 		this.handlers = new CompositeLogoutHandler(handlers);
 	}

+ 9 - 0
web/src/test/java/org/springframework/security/MockSecurityContextHolderStrategy.java

@@ -16,6 +16,7 @@
 
 package org.springframework.security;
 
+import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContext;
 import org.springframework.security.core.context.SecurityContextHolderStrategy;
 import org.springframework.security.core.context.SecurityContextImpl;
@@ -24,6 +25,14 @@ public class MockSecurityContextHolderStrategy implements SecurityContextHolderS
 
 	private SecurityContext mock;
 
+	public MockSecurityContextHolderStrategy() {
+
+	}
+
+	public MockSecurityContextHolderStrategy(Authentication authentication) {
+		this.mock = new SecurityContextImpl(authentication);
+	}
+
 	@Override
 	public void clearContext() {
 		this.mock = null;

+ 24 - 1
web/src/test/java/org/springframework/security/web/concurrent/ConcurrentSessionFilterTests.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.
@@ -28,7 +28,10 @@ import org.springframework.mock.web.MockFilterChain;
 import org.springframework.mock.web.MockHttpServletRequest;
 import org.springframework.mock.web.MockHttpServletResponse;
 import org.springframework.mock.web.MockHttpSession;
+import org.springframework.security.MockSecurityContextHolderStrategy;
+import org.springframework.security.authentication.TestingAuthenticationToken;
 import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.context.SecurityContextHolderStrategy;
 import org.springframework.security.core.session.SessionInformation;
 import org.springframework.security.core.session.SessionRegistry;
 import org.springframework.security.core.session.SessionRegistryImpl;
@@ -46,6 +49,7 @@ import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.BDDMockito.given;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyZeroInteractions;
 
@@ -266,6 +270,25 @@ public class ConcurrentSessionFilterTests {
 		verify(handler).logout(eq(request), eq(response), any());
 	}
 
+	@Test
+	public void doFilterWhenCustomSecurityContextHolderStrategyThenHandlersUsed() throws Exception {
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		MockHttpSession session = new MockHttpSession();
+		request.setSession(session);
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		SessionRegistry registry = mock(SessionRegistry.class);
+		SessionInformation information = new SessionInformation("user", "sessionId",
+				new Date(System.currentTimeMillis() - 1000));
+		information.expireNow();
+		given(registry.getSessionInformation(anyString())).willReturn(information);
+		ConcurrentSessionFilter filter = new ConcurrentSessionFilter(registry);
+		SecurityContextHolderStrategy securityContextHolderStrategy = spy(
+				new MockSecurityContextHolderStrategy(new TestingAuthenticationToken("user", "password")));
+		filter.setSecurityContextHolderStrategy(securityContextHolderStrategy);
+		filter.doFilter(request, response, new MockFilterChain());
+		verify(securityContextHolderStrategy).getContext();
+	}
+
 	@Test
 	public void setLogoutHandlersWhenNullThenThrowsException() {
 		ConcurrentSessionFilter filter = new ConcurrentSessionFilter(new SessionRegistryImpl());