From 56b9badcfe2a2339ce33ac7c398d2f246f4fdafb Mon Sep 17 00:00:00 2001 From: Evgeniy Cheban Date: Thu, 6 Oct 2022 02:31:55 +0200 Subject: [PATCH] AnonymousAuthenticationFilter should cache its Supplier Closes gh-11900 --- .../AnonymousAuthenticationFilter.java | 6 +++-- .../MockSecurityContextHolderStrategy.java | 26 +++++++++++++++---- .../AnonymousAuthenticationFilterTests.java | 22 ++++++++++++++++ 3 files changed, 47 insertions(+), 7 deletions(-) diff --git a/web/src/main/java/org/springframework/security/web/authentication/AnonymousAuthenticationFilter.java b/web/src/main/java/org/springframework/security/web/authentication/AnonymousAuthenticationFilter.java index a0ae8554aa..55ec8a6ef5 100644 --- a/web/src/main/java/org/springframework/security/web/authentication/AnonymousAuthenticationFilter.java +++ b/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 defaultWithAnonymous(HttpServletRequest request, Supplier currentDeferredContext) { - return () -> { + return SingletonSupplier.of(() -> { SecurityContext currentContext = currentDeferredContext.get(); return defaultWithAnonymous(request, currentContext); - }; + }); } private SecurityContext defaultWithAnonymous(HttpServletRequest request, SecurityContext currentContext) { diff --git a/web/src/test/java/org/springframework/security/MockSecurityContextHolderStrategy.java b/web/src/test/java/org/springframework/security/MockSecurityContextHolderStrategy.java index 468f2ba659..a347f6d470 100644 --- a/web/src/test/java/org/springframework/security/MockSecurityContextHolderStrategy.java +++ b/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 mock; public MockSecurityContextHolderStrategy() { } public MockSecurityContextHolderStrategy(Authentication authentication) { - this.mock = new SecurityContextImpl(authentication); + this(() -> new SecurityContextImpl(authentication)); + } + + public MockSecurityContextHolderStrategy(Supplier 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 getDeferredContext() { + return this.mock; + } + + @Override + public void setDeferredContext(Supplier deferredContext) { + this.mock = deferredContext; + } + } diff --git a/web/src/test/java/org/springframework/security/web/authentication/AnonymousAuthenticationFilterTests.java b/web/src/test/java/org/springframework/security/web/authentication/AnonymousAuthenticationFilterTests.java index 2dd4c2e30e..e0d97b8092 100644 --- a/web/src/test/java/org/springframework/security/web/authentication/AnonymousAuthenticationFilterTests.java +++ b/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 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 deferredContext = strategy.getDeferredContext(); + deferredContext.get(); + deferredContext.get(); + verify(originalSupplier, times(1)).get(); + } + private class MockFilterChain implements FilterChain { private boolean expectToProceed;