Jelajahi Sumber

AnonymousAuthenticationFilter should cache its Supplier<SecurityContext>

Closes gh-11900
Evgeniy Cheban 2 tahun lalu
induk
melakukan
56b9badcfe

+ 4 - 2
web/src/main/java/org/springframework/security/web/authentication/AnonymousAuthenticationFilter.java

@@ -37,6 +37,7 @@ import org.springframework.security.core.context.SecurityContext;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.core.context.SecurityContextHolderStrategy;
 import org.springframework.util.Assert;
+import org.springframework.util.function.SingletonSupplier;
 import org.springframework.web.filter.GenericFilterBean;
 
 /**
@@ -45,6 +46,7 @@ import org.springframework.web.filter.GenericFilterBean;
  *
  * @author Ben Alex
  * @author Luke Taylor
+ * @author Evgeniy Cheban
  */
 public class AnonymousAuthenticationFilter extends GenericFilterBean implements InitializingBean {
 
@@ -100,10 +102,10 @@ public class AnonymousAuthenticationFilter extends GenericFilterBean implements
 
 	private Supplier<SecurityContext> defaultWithAnonymous(HttpServletRequest request,
 			Supplier<SecurityContext> currentDeferredContext) {
-		return () -> {
+		return SingletonSupplier.of(() -> {
 			SecurityContext currentContext = currentDeferredContext.get();
 			return defaultWithAnonymous(request, currentContext);
-		};
+		});
 	}
 
 	private SecurityContext defaultWithAnonymous(HttpServletRequest request, SecurityContext currentContext) {

+ 21 - 5
web/src/test/java/org/springframework/security/MockSecurityContextHolderStrategy.java

@@ -16,6 +16,8 @@
 
 package org.springframework.security;
 
+import java.util.function.Supplier;
+
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContext;
 import org.springframework.security.core.context.SecurityContextHolderStrategy;
@@ -23,29 +25,33 @@ import org.springframework.security.core.context.SecurityContextImpl;
 
 public class MockSecurityContextHolderStrategy implements SecurityContextHolderStrategy {
 
-	private SecurityContext mock;
+	private Supplier<SecurityContext> mock;
 
 	public MockSecurityContextHolderStrategy() {
 
 	}
 
 	public MockSecurityContextHolderStrategy(Authentication authentication) {
-		this.mock = new SecurityContextImpl(authentication);
+		this(() -> new SecurityContextImpl(authentication));
+	}
+
+	public MockSecurityContextHolderStrategy(Supplier<SecurityContext> mock) {
+		this.mock = mock;
 	}
 
 	@Override
 	public void clearContext() {
-		this.mock = null;
+		this.mock = () -> null;
 	}
 
 	@Override
 	public SecurityContext getContext() {
-		return this.mock;
+		return this.mock.get();
 	}
 
 	@Override
 	public void setContext(SecurityContext context) {
-		this.mock = context;
+		this.mock = () -> context;
 	}
 
 	@Override
@@ -53,4 +59,14 @@ public class MockSecurityContextHolderStrategy implements SecurityContextHolderS
 		return new SecurityContextImpl();
 	}
 
+	@Override
+	public Supplier<SecurityContext> getDeferredContext() {
+		return this.mock;
+	}
+
+	@Override
+	public void setDeferredContext(Supplier<SecurityContext> deferredContext) {
+		this.mock = deferredContext;
+	}
+
 }

+ 22 - 0
web/src/test/java/org/springframework/security/web/authentication/AnonymousAuthenticationFilterTests.java

@@ -32,6 +32,7 @@ import org.junit.jupiter.api.Test;
 
 import org.springframework.mock.web.MockHttpServletRequest;
 import org.springframework.mock.web.MockHttpServletResponse;
+import org.springframework.security.MockSecurityContextHolderStrategy;
 import org.springframework.security.authentication.TestingAuthenticationToken;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.authority.AuthorityUtils;
@@ -46,6 +47,7 @@ import static org.assertj.core.api.Assertions.fail;
 import static org.mockito.BDDMockito.given;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 /**
@@ -53,6 +55,7 @@ import static org.mockito.Mockito.verify;
  *
  * @author Ben Alex
  * @author Eddú Meléndez
+ * @author Evgeniy Cheban
  */
 public class AnonymousAuthenticationFilterTests {
 
@@ -128,6 +131,25 @@ public class AnonymousAuthenticationFilterTests {
 		verify(originalSupplier, never()).get();
 	}
 
+	@Test
+	public void doFilterSetsSingletonSupplier() throws Exception {
+		Supplier<SecurityContext> originalSupplier = mock(Supplier.class);
+		Authentication originalAuth = new TestingAuthenticationToken("user", "password", "ROLE_A");
+		SecurityContext originalContext = new SecurityContextImpl(originalAuth);
+		SecurityContextHolderStrategy strategy = new MockSecurityContextHolderStrategy(originalSupplier);
+		given(originalSupplier.get()).willReturn(originalContext);
+		AnonymousAuthenticationFilter filter = new AnonymousAuthenticationFilter("qwerty", "anonymousUsername",
+				AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
+		filter.setSecurityContextHolderStrategy(strategy);
+		filter.afterPropertiesSet();
+		executeFilterInContainerSimulator(mock(FilterConfig.class), filter, new MockHttpServletRequest(),
+				new MockHttpServletResponse(), new MockFilterChain(true));
+		Supplier<SecurityContext> deferredContext = strategy.getDeferredContext();
+		deferredContext.get();
+		deferredContext.get();
+		verify(originalSupplier, times(1)).get();
+	}
+
 	private class MockFilterChain implements FilterChain {
 
 		private boolean expectToProceed;