From 3f774548d2359cd67aa9cfabf0e7245e802bbd1f Mon Sep 17 00:00:00 2001 From: Josh Cummings <3627351+jzheaux@users.noreply.github.com> Date: Fri, 29 Aug 2025 13:14:21 -0600 Subject: [PATCH] Move Authority Propagation Into Filters Given that the filters are the level at which the SecurityContextHolder is consulted, this commit moves the operation that ProviderManager was doing into each authentication filter. Issue gh-17862 --- .../AuthenticationManagerBuilder.java | 30 -------------- .../AuthenticationConfiguration.java | 2 - .../GlobalMethodSecurityConfiguration.java | 1 - .../HttpSecurityConfiguration.java | 2 - .../web/configurers/WebAuthnConfigurer.java | 6 +-- .../AuthenticationManagerFactoryBean.java | 6 --- .../AbstractAuthenticationToken.java | 2 + ...legatingReactiveAuthenticationManager.java | 15 ------- .../authentication/ProviderManager.java | 37 ++++-------------- ...ingReactiveAuthenticationManagerTests.java | 21 ---------- .../authentication/ProviderManagerTests.java | 39 ++++++------------- ...bstractAuthenticationProcessingFilter.java | 6 +++ .../authentication/AuthenticationFilter.java | 6 +++ ...tractPreAuthenticatedProcessingFilter.java | 6 +++ .../www/BasicAuthenticationFilter.java | 4 ++ .../AuthenticationWebFilter.java | 14 +++++++ .../AuthenticationFilterTests.java | 1 + 17 files changed, 60 insertions(+), 138 deletions(-) diff --git a/config/src/main/java/org/springframework/security/config/annotation/authentication/builders/AuthenticationManagerBuilder.java b/config/src/main/java/org/springframework/security/config/annotation/authentication/builders/AuthenticationManagerBuilder.java index 961cf67c56..64f5fd489b 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/authentication/builders/AuthenticationManagerBuilder.java +++ b/config/src/main/java/org/springframework/security/config/annotation/authentication/builders/AuthenticationManagerBuilder.java @@ -18,14 +18,10 @@ package org.springframework.security.config.annotation.authentication.builders; import java.util.ArrayList; import java.util.List; -import java.util.stream.Stream; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.jspecify.annotations.Nullable; -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.ObjectProvider; import org.springframework.security.authentication.AuthenticationEventPublisher; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationProvider; @@ -41,8 +37,6 @@ import org.springframework.security.config.annotation.authentication.configurers import org.springframework.security.config.annotation.authentication.configurers.userdetails.DaoAuthenticationConfigurer; import org.springframework.security.config.annotation.authentication.configurers.userdetails.UserDetailsAwareConfigurer; 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.userdetails.UserDetailsService; import org.springframework.util.Assert; @@ -241,10 +235,6 @@ public class AuthenticationManagerBuilder if (this.eventPublisher != null) { providerManager.setAuthenticationEventPublisher(this.eventPublisher); } - SecurityContextHolderStrategy securityContextHolderStrategy = getBeanProvider( - SecurityContextHolderStrategy.class) - .getIfUnique(SecurityContextHolder::getContextHolderStrategy); - providerManager.setSecurityContextHolderStrategy(securityContextHolderStrategy); providerManager = postProcess(providerManager); return providerManager; } @@ -293,24 +283,4 @@ public class AuthenticationManagerBuilder return configurer; } - private ObjectProvider getBeanProvider(Class clazz) { - BeanFactory beanFactory = getSharedObject(BeanFactory.class); - return (beanFactory != null) ? beanFactory.getBeanProvider(clazz) : new SingleObjectProvider<>(null); - } - - private static final class SingleObjectProvider implements ObjectProvider { - - private final @Nullable O object; - - private SingleObjectProvider(@Nullable O object) { - this.object = object; - } - - @Override - public Stream stream() { - return Stream.ofNullable(this.object); - } - - } - } diff --git a/config/src/main/java/org/springframework/security/config/annotation/authentication/configuration/AuthenticationConfiguration.java b/config/src/main/java/org/springframework/security/config/annotation/authentication/configuration/AuthenticationConfiguration.java index 72f9e8ac88..77b5f38986 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/authentication/configuration/AuthenticationConfiguration.java +++ b/config/src/main/java/org/springframework/security/config/annotation/authentication/configuration/AuthenticationConfiguration.java @@ -27,7 +27,6 @@ import org.apache.commons.logging.LogFactory; import org.springframework.aop.framework.ProxyFactoryBean; import org.springframework.aop.target.LazyInitTargetSource; -import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; @@ -84,7 +83,6 @@ public class AuthenticationConfiguration { AuthenticationEventPublisher authenticationEventPublisher = getAuthenticationEventPublisher(context); DefaultPasswordEncoderAuthenticationManagerBuilder result = new DefaultPasswordEncoderAuthenticationManagerBuilder( objectPostProcessor, defaultPasswordEncoder); - result.setSharedObject(BeanFactory.class, this.applicationContext); if (authenticationEventPublisher != null) { result.authenticationEventPublisher(authenticationEventPublisher); } diff --git a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfiguration.java b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfiguration.java index fe1c4028d0..7b0fc0a13c 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfiguration.java +++ b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfiguration.java @@ -318,7 +318,6 @@ public class GlobalMethodSecurityConfiguration implements ImportAware, SmartInit .postProcess(new DefaultAuthenticationEventPublisher()); this.auth = new AuthenticationManagerBuilder(this.objectPostProcessor); this.auth.authenticationEventPublisher(eventPublisher); - this.auth.setSharedObject(BeanFactory.class, this.context); configure(this.auth); this.authenticationManager = (this.disableAuthenticationRegistry) ? getAuthenticationConfiguration().getAuthenticationManager() : this.auth.build(); diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configuration/HttpSecurityConfiguration.java b/config/src/main/java/org/springframework/security/config/annotation/web/configuration/HttpSecurityConfiguration.java index 791b45566c..ad641ea656 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configuration/HttpSecurityConfiguration.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configuration/HttpSecurityConfiguration.java @@ -21,7 +21,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; @@ -117,7 +116,6 @@ class HttpSecurityConfiguration { LazyPasswordEncoder passwordEncoder = new LazyPasswordEncoder(this.context); AuthenticationManagerBuilder authenticationBuilder = new DefaultPasswordEncoderAuthenticationManagerBuilder( this.objectPostProcessor, passwordEncoder); - authenticationBuilder.setSharedObject(BeanFactory.class, this.context); authenticationBuilder.parentAuthenticationManager(authenticationManager()); authenticationBuilder.authenticationEventPublisher(getAuthenticationEventPublisher()); HttpSecurity http = new HttpSecurity(this.objectPostProcessor, authenticationBuilder, createSharedObjects()); diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/WebAuthnConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/WebAuthnConfigurer.java index c23fbc230c..7ec3279efb 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/WebAuthnConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/WebAuthnConfigurer.java @@ -162,10 +162,8 @@ public class WebAuthnConfigurer> WebAuthnRelyingPartyOperations rpOperations = webAuthnRelyingPartyOperations(userEntities, userCredentials); PublicKeyCredentialCreationOptionsRepository creationOptionsRepository = creationOptionsRepository(); WebAuthnAuthenticationFilter webAuthnAuthnFilter = new WebAuthnAuthenticationFilter(); - ProviderManager manager = new ProviderManager( - new WebAuthnAuthenticationProvider(rpOperations, userDetailsService)); - manager.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy()); - webAuthnAuthnFilter.setAuthenticationManager(manager); + webAuthnAuthnFilter.setAuthenticationManager( + new ProviderManager(new WebAuthnAuthenticationProvider(rpOperations, userDetailsService))); WebAuthnRegistrationFilter webAuthnRegistrationFilter = new WebAuthnRegistrationFilter(userCredentials, rpOperations); PublicKeyCredentialCreationOptionsFilter creationOptionsFilter = new PublicKeyCredentialCreationOptionsFilter( diff --git a/config/src/main/java/org/springframework/security/config/authentication/AuthenticationManagerFactoryBean.java b/config/src/main/java/org/springframework/security/config/authentication/AuthenticationManagerFactoryBean.java index 14e3b5f53b..afa0b11bea 100644 --- a/config/src/main/java/org/springframework/security/config/authentication/AuthenticationManagerFactoryBean.java +++ b/config/src/main/java/org/springframework/security/config/authentication/AuthenticationManagerFactoryBean.java @@ -30,8 +30,6 @@ import org.springframework.security.authentication.ObservationAuthenticationMana import org.springframework.security.authentication.ProviderManager; import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.config.BeanIds; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.core.context.SecurityContextHolderStrategy; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.password.PasswordEncoder; @@ -74,10 +72,6 @@ public class AuthenticationManagerFactoryBean implements FactoryBean authorities; diff --git a/core/src/main/java/org/springframework/security/authentication/DelegatingReactiveAuthenticationManager.java b/core/src/main/java/org/springframework/security/authentication/DelegatingReactiveAuthenticationManager.java index 1a40d210e0..c30fcbae3e 100644 --- a/core/src/main/java/org/springframework/security/authentication/DelegatingReactiveAuthenticationManager.java +++ b/core/src/main/java/org/springframework/security/authentication/DelegatingReactiveAuthenticationManager.java @@ -27,7 +27,6 @@ import reactor.core.publisher.Mono; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.context.ReactiveSecurityContextHolder; import org.springframework.util.Assert; /** @@ -58,20 +57,6 @@ public class DelegatingReactiveAuthenticationManager implements ReactiveAuthenti @Override public Mono authenticate(Authentication authentication) { - return ReactiveSecurityContextHolder.getContext().flatMap((context) -> { - Mono result = doAuthenticate(authentication); - Authentication current = context.getAuthentication(); - if (current == null) { - return result; - } - if (!current.isAuthenticated()) { - return result; - } - return doAuthenticate(current).map((r) -> r.toBuilder().apply(current).build()); - }).switchIfEmpty(doAuthenticate(authentication)); - } - - private Mono doAuthenticate(Authentication authentication) { Flux result = Flux.fromIterable(this.delegates); Function> logging = (m) -> m.authenticate(authentication) .doOnError(AuthenticationException.class, (ex) -> ex.setAuthenticationRequest(authentication)) diff --git a/core/src/main/java/org/springframework/security/authentication/ProviderManager.java b/core/src/main/java/org/springframework/security/authentication/ProviderManager.java index 417d144849..7167943a33 100644 --- a/core/src/main/java/org/springframework/security/authentication/ProviderManager.java +++ b/core/src/main/java/org/springframework/security/authentication/ProviderManager.java @@ -33,8 +33,6 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.CredentialsContainer; import org.springframework.security.core.SpringSecurityMessageSource; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.core.context.SecurityContextHolderStrategy; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; @@ -94,9 +92,6 @@ public class ProviderManager implements AuthenticationManager, MessageSourceAwar private static final Log logger = LogFactory.getLog(ProviderManager.class); - private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder - .getContextHolderStrategy(); - private AuthenticationEventPublisher eventPublisher = new NullEventPublisher(); private List providers = Collections.emptyList(); @@ -187,7 +182,7 @@ public class ProviderManager implements AuthenticationManager, MessageSourceAwar try { result = provider.authenticate(authentication); if (result != null) { - copyDetails(authentication, result); + result = copyDetails(authentication, result); break; } } @@ -214,7 +209,6 @@ public class ProviderManager implements AuthenticationManager, MessageSourceAwar lastException = ex; } } - result = applyPreviousAuthentication(result); if (result == null && this.parent != null) { // Allow the parent to try. try { @@ -271,20 +265,6 @@ public class ProviderManager implements AuthenticationManager, MessageSourceAwar throw lastException; } - private @Nullable Authentication applyPreviousAuthentication(@Nullable Authentication result) { - if (result == null) { - return null; - } - Authentication current = this.securityContextHolderStrategy.getContext().getAuthentication(); - if (current == null) { - return result; - } - if (!current.isAuthenticated()) { - return result; - } - return result.toBuilder().apply(current).build(); - } - @SuppressWarnings("deprecation") private void prepareException(AuthenticationException ex, Authentication auth) { ex.setAuthenticationRequest(auth); @@ -297,21 +277,20 @@ public class ProviderManager implements AuthenticationManager, MessageSourceAwar * @param source source authentication * @param dest the destination authentication object */ - private void copyDetails(Authentication source, Authentication dest) { - if ((dest instanceof AbstractAuthenticationToken token) && (dest.getDetails() == null)) { - token.setDetails(source.getDetails()); + private Authentication copyDetails(Authentication source, Authentication dest) { + if (source.getDetails() == null) { + return dest; } + if (dest.getDetails() != null) { + return dest; + } + return dest.toBuilder().details(source.getDetails()).build(); } public List getProviders() { return this.providers; } - public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) { - Assert.notNull(securityContextHolderStrategy, "securityContextHolderStrategy cannot be null"); - this.securityContextHolderStrategy = securityContextHolderStrategy; - } - @Override public void setMessageSource(MessageSource messageSource) { this.messages = new MessageSourceAccessor(messageSource); diff --git a/core/src/test/java/org/springframework/security/authentication/DelegatingReactiveAuthenticationManagerTests.java b/core/src/test/java/org/springframework/security/authentication/DelegatingReactiveAuthenticationManagerTests.java index 6c430bdf03..d44278a60f 100644 --- a/core/src/test/java/org/springframework/security/authentication/DelegatingReactiveAuthenticationManagerTests.java +++ b/core/src/test/java/org/springframework/security/authentication/DelegatingReactiveAuthenticationManagerTests.java @@ -27,13 +27,10 @@ import reactor.test.StepVerifier; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.context.ReactiveSecurityContextHolder; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; /** * @author Rob Winch @@ -121,24 +118,6 @@ public class DelegatingReactiveAuthenticationManagerTests { assertThat(expected.getAuthenticationRequest()).isEqualTo(this.authentication); } - @Test - void authenticateWhenPreviousAuthenticationThenApplies() { - Authentication factorOne = new TestingAuthenticationToken("user", "pass", "FACTOR_ONE"); - Authentication factorTwo = new TestingAuthenticationToken("user", "pass", "FACTOR_TWO"); - ReactiveAuthenticationManager provider = mock(ReactiveAuthenticationManager.class); - given(provider.authenticate(any())).willReturn(Mono.just(factorTwo)); - ReactiveAuthenticationManager manager = new DelegatingReactiveAuthenticationManager(provider); - Authentication request = new TestingAuthenticationToken("user", "password"); - StepVerifier - .create(manager.authenticate(request) - .flatMapIterable(Authentication::getAuthorities) - .map(GrantedAuthority::getAuthority) - .contextWrite(ReactiveSecurityContextHolder.withAuthentication(factorOne))) - .expectNext("FACTOR_TWO") - .expectNext("FACTOR_ONE") - .verifyComplete(); - } - private DelegatingReactiveAuthenticationManager managerWithContinueOnError() { DelegatingReactiveAuthenticationManager manager = new DelegatingReactiveAuthenticationManager(this.delegate1, this.delegate2); diff --git a/core/src/test/java/org/springframework/security/authentication/ProviderManagerTests.java b/core/src/test/java/org/springframework/security/authentication/ProviderManagerTests.java index e1c69c5fe7..69fff2567c 100644 --- a/core/src/test/java/org/springframework/security/authentication/ProviderManagerTests.java +++ b/core/src/test/java/org/springframework/security/authentication/ProviderManagerTests.java @@ -18,17 +18,15 @@ package org.springframework.security.authentication; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.List; -import java.util.Set; import org.junit.jupiter.api.Test; import org.springframework.context.MessageSource; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.core.context.SecurityContextHolderStrategy; -import org.springframework.security.core.context.SecurityContextImpl; +import org.springframework.security.core.GrantedAuthority; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -51,7 +49,7 @@ public class ProviderManagerTests { @Test void authenticationFailsWithUnsupportedToken() { - Authentication token = new AbstractAuthenticationToken(null) { + Authentication token = new AbstractAuthenticationToken((Collection) null) { @Override public Object getCredentials() { return ""; @@ -82,24 +80,24 @@ public class ProviderManagerTests { @Test void authenticationSucceedsWithSupportedTokenAndReturnsExpectedObject() { - Authentication a = mock(Authentication.class); + Authentication a = new TestingAuthenticationToken("user", "pass", "FACTOR"); ProviderManager mgr = new ProviderManager(createProviderWhichReturns(a)); AuthenticationEventPublisher publisher = mock(AuthenticationEventPublisher.class); mgr.setAuthenticationEventPublisher(publisher); Authentication result = mgr.authenticate(a); - assertThat(result).isEqualTo(a); + assertThat(result.getPrincipal()).isEqualTo(a.getPrincipal()); verify(publisher).publishAuthenticationSuccess(result); } @Test void authenticationSucceedsWhenFirstProviderReturnsNullButSecondAuthenticates() { - Authentication a = mock(Authentication.class); + Authentication a = new TestingAuthenticationToken("user", "pass", "FACTOR"); ProviderManager mgr = new ProviderManager( Arrays.asList(createProviderWhichReturns(null), createProviderWhichReturns(a))); AuthenticationEventPublisher publisher = mock(AuthenticationEventPublisher.class); mgr.setAuthenticationEventPublisher(publisher); Authentication result = mgr.authenticate(a); - assertThat(result).isSameAs(a); + assertThat(result.getPrincipal()).isEqualTo(a.getPrincipal()); verify(publisher).publishAuthenticationSuccess(result); } @@ -166,11 +164,12 @@ public class ProviderManagerTests { @Test void authenticationExceptionIsIgnoredIfLaterProviderAuthenticates() { - Authentication authReq = mock(Authentication.class); + Authentication result = new TestingAuthenticationToken("user", "pass", "FACTOR"); ProviderManager mgr = new ProviderManager( createProviderWhichThrows(new BadCredentialsException("", new Throwable())), - createProviderWhichReturns(authReq)); - assertThat(mgr.authenticate(mock(Authentication.class))).isSameAs(authReq); + createProviderWhichReturns(result)); + Authentication request = new TestingAuthenticationToken("user", "pass"); + assertThat(mgr.authenticate(request).getPrincipal()).isEqualTo(result.getPrincipal()); } @Test @@ -314,22 +313,6 @@ public class ProviderManagerTests { verifyNoMoreInteractions(publisher); // Child should not publish (duplicate event) } - @Test - void authenticateWhenPreviousAuthenticationThenApplies() { - Authentication factorOne = new TestingAuthenticationToken("user", "pass", "FACTOR_ONE"); - Authentication factorTwo = new TestingAuthenticationToken("user", "pass", "FACTOR_TWO"); - SecurityContextHolderStrategy securityContextHolderStrategy = mock(SecurityContextHolderStrategy.class); - given(securityContextHolderStrategy.getContext()).willReturn(new SecurityContextImpl(factorOne)); - AuthenticationProvider provider = mock(AuthenticationProvider.class); - given(provider.authenticate(any())).willReturn(factorTwo); - given(provider.supports(any())).willReturn(true); - ProviderManager manager = new ProviderManager(provider); - manager.setSecurityContextHolderStrategy(securityContextHolderStrategy); - Authentication request = new TestingAuthenticationToken("user", "password"); - Set authorities = AuthorityUtils.authorityListToSet(manager.authenticate(request).getAuthorities()); - assertThat(authorities).containsExactlyInAnyOrder("FACTOR_ONE", "FACTOR_TWO"); - } - private AuthenticationProvider createProviderWhichThrows(final AuthenticationException ex) { AuthenticationProvider provider = mock(AuthenticationProvider.class); given(provider.supports(any(Class.class))).willReturn(true); diff --git a/web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.java b/web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.java index a817d7013e..6bbbb3f766 100644 --- a/web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.java +++ b/web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.java @@ -248,6 +248,12 @@ public abstract class AbstractAuthenticationProcessingFilter extends GenericFilt // return immediately as subclass has indicated that it hasn't completed return; } + Authentication current = this.securityContextHolderStrategy.getContext().getAuthentication(); + if (current != null && current.isAuthenticated()) { + authenticationResult = authenticationResult.toBuilder() + .authorities((a) -> a.addAll(current.getAuthorities())) + .build(); + } this.sessionStrategy.onAuthentication(authenticationResult, request, response); // Authentication success if (this.continueChainBeforeSuccessfulAuthentication) { diff --git a/web/src/main/java/org/springframework/security/web/authentication/AuthenticationFilter.java b/web/src/main/java/org/springframework/security/web/authentication/AuthenticationFilter.java index 5366ebd84b..8295a962bc 100644 --- a/web/src/main/java/org/springframework/security/web/authentication/AuthenticationFilter.java +++ b/web/src/main/java/org/springframework/security/web/authentication/AuthenticationFilter.java @@ -184,6 +184,12 @@ public class AuthenticationFilter extends OncePerRequestFilter { filterChain.doFilter(request, response); return; } + Authentication current = this.securityContextHolderStrategy.getContext().getAuthentication(); + if (current != null && current.isAuthenticated()) { + authenticationResult = authenticationResult.toBuilder() + .authorities((a) -> a.addAll(current.getAuthorities())) + .build(); + } HttpSession session = request.getSession(false); if (session != null) { request.changeSessionId(); diff --git a/web/src/main/java/org/springframework/security/web/authentication/preauth/AbstractPreAuthenticatedProcessingFilter.java b/web/src/main/java/org/springframework/security/web/authentication/preauth/AbstractPreAuthenticatedProcessingFilter.java index 8189cc2b1f..b0910c85ed 100755 --- a/web/src/main/java/org/springframework/security/web/authentication/preauth/AbstractPreAuthenticatedProcessingFilter.java +++ b/web/src/main/java/org/springframework/security/web/authentication/preauth/AbstractPreAuthenticatedProcessingFilter.java @@ -204,6 +204,12 @@ public abstract class AbstractPreAuthenticatedProcessingFilter extends GenericFi principal, credentials); authenticationRequest.setDetails(this.authenticationDetailsSource.buildDetails(request)); Authentication authenticationResult = this.authenticationManager.authenticate(authenticationRequest); + Authentication current = this.securityContextHolderStrategy.getContext().getAuthentication(); + if (current != null && current.isAuthenticated()) { + authenticationResult = authenticationResult.toBuilder() + .authorities((a) -> a.addAll(current.getAuthorities())) + .build(); + } successfulAuthentication(request, response, authenticationResult); } catch (AuthenticationException ex) { diff --git a/web/src/main/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilter.java b/web/src/main/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilter.java index 98fdd58da1..91f40b01b7 100644 --- a/web/src/main/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilter.java +++ b/web/src/main/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilter.java @@ -186,6 +186,10 @@ public class BasicAuthenticationFilter extends OncePerRequestFilter { this.logger.trace(LogMessage.format("Found username '%s' in Basic Authorization header", username)); if (authenticationIsRequired(username)) { Authentication authResult = this.authenticationManager.authenticate(authRequest); + Authentication current = this.securityContextHolderStrategy.getContext().getAuthentication(); + if (current != null && current.isAuthenticated()) { + authResult = authResult.toBuilder().authorities((a) -> a.addAll(current.getAuthorities())).build(); + } SecurityContext context = this.securityContextHolderStrategy.createEmptyContext(); context.setAuthentication(authResult); this.securityContextHolderStrategy.setContext(context); diff --git a/web/src/main/java/org/springframework/security/web/server/authentication/AuthenticationWebFilter.java b/web/src/main/java/org/springframework/security/web/server/authentication/AuthenticationWebFilter.java index 3bbbdb108c..2f83629181 100644 --- a/web/src/main/java/org/springframework/security/web/server/authentication/AuthenticationWebFilter.java +++ b/web/src/main/java/org/springframework/security/web/server/authentication/AuthenticationWebFilter.java @@ -122,12 +122,26 @@ public class AuthenticationWebFilter implements WebFilter { .flatMap((authenticationManager) -> authenticationManager.authenticate(token)) .switchIfEmpty(Mono .defer(() -> Mono.error(new IllegalStateException("No provider found for " + token.getClass())))) + .flatMap(this::applyCurrentAuthenication) .flatMap( (authentication) -> onAuthenticationSuccess(authentication, new WebFilterExchange(exchange, chain))) .doOnError(AuthenticationException.class, (ex) -> logger.debug(LogMessage.format("Authentication failed: %s", ex.getMessage()), ex)); } + private Mono applyCurrentAuthenication(Authentication result) { + return ReactiveSecurityContextHolder.getContext().map((context) -> { + Authentication current = context.getAuthentication(); + if (current == null) { + return result; + } + if (!current.isAuthenticated()) { + return result; + } + return result.toBuilder().authorities((a) -> a.addAll(current.getAuthorities())).build(); + }).switchIfEmpty(Mono.just(result)); + } + protected Mono onAuthenticationSuccess(Authentication authentication, WebFilterExchange webFilterExchange) { ServerWebExchange exchange = webFilterExchange.getExchange(); SecurityContextImpl securityContext = new SecurityContextImpl(); diff --git a/web/src/test/java/org/springframework/security/web/authentication/AuthenticationFilterTests.java b/web/src/test/java/org/springframework/security/web/authentication/AuthenticationFilterTests.java index 3851311bee..aaf17d62c9 100644 --- a/web/src/test/java/org/springframework/security/web/authentication/AuthenticationFilterTests.java +++ b/web/src/test/java/org/springframework/security/web/authentication/AuthenticationFilterTests.java @@ -144,6 +144,7 @@ public class AuthenticationFilterTests { this.authenticationConverter); SecurityContextHolderStrategy strategy = mock(SecurityContextHolderStrategy.class); given(strategy.createEmptyContext()).willReturn(new SecurityContextImpl()); + given(strategy.getContext()).willReturn(new SecurityContextImpl()); filter.setSecurityContextHolderStrategy(strategy); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/"); MockHttpServletResponse response = new MockHttpServletResponse();