ソースを参照

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
Josh Cummings 3 週間 前
コミット
3f774548d2
17 ファイル変更60 行追加138 行削除
  1. 0 30
      config/src/main/java/org/springframework/security/config/annotation/authentication/builders/AuthenticationManagerBuilder.java
  2. 0 2
      config/src/main/java/org/springframework/security/config/annotation/authentication/configuration/AuthenticationConfiguration.java
  3. 0 1
      config/src/main/java/org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfiguration.java
  4. 0 2
      config/src/main/java/org/springframework/security/config/annotation/web/configuration/HttpSecurityConfiguration.java
  5. 2 4
      config/src/main/java/org/springframework/security/config/annotation/web/configurers/WebAuthnConfigurer.java
  6. 0 6
      config/src/main/java/org/springframework/security/config/authentication/AuthenticationManagerFactoryBean.java
  7. 2 0
      core/src/main/java/org/springframework/security/authentication/AbstractAuthenticationToken.java
  8. 0 15
      core/src/main/java/org/springframework/security/authentication/DelegatingReactiveAuthenticationManager.java
  9. 8 29
      core/src/main/java/org/springframework/security/authentication/ProviderManager.java
  10. 0 21
      core/src/test/java/org/springframework/security/authentication/DelegatingReactiveAuthenticationManagerTests.java
  11. 11 28
      core/src/test/java/org/springframework/security/authentication/ProviderManagerTests.java
  12. 6 0
      web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.java
  13. 6 0
      web/src/main/java/org/springframework/security/web/authentication/AuthenticationFilter.java
  14. 6 0
      web/src/main/java/org/springframework/security/web/authentication/preauth/AbstractPreAuthenticatedProcessingFilter.java
  15. 4 0
      web/src/main/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilter.java
  16. 14 0
      web/src/main/java/org/springframework/security/web/server/authentication/AuthenticationWebFilter.java
  17. 1 0
      web/src/test/java/org/springframework/security/web/authentication/AuthenticationFilterTests.java

+ 0 - 30
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 <C> ObjectProvider<C> getBeanProvider(Class<C> clazz) {
-		BeanFactory beanFactory = getSharedObject(BeanFactory.class);
-		return (beanFactory != null) ? beanFactory.getBeanProvider(clazz) : new SingleObjectProvider<>(null);
-	}
-
-	private static final class SingleObjectProvider<O> implements ObjectProvider<O> {
-
-		private final @Nullable O object;
-
-		private SingleObjectProvider(@Nullable O object) {
-			this.object = object;
-		}
-
-		@Override
-		public Stream<O> stream() {
-			return Stream.ofNullable(this.object);
-		}
-
-	}
-
 }

+ 0 - 2
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);
 		}

+ 0 - 1
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();

+ 0 - 2
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());

+ 2 - 4
config/src/main/java/org/springframework/security/config/annotation/web/configurers/WebAuthnConfigurer.java

@@ -162,10 +162,8 @@ public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
 		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(

+ 0 - 6
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<Authenticat
 			}
 			provider.afterPropertiesSet();
 			ProviderManager manager = new ProviderManager(Arrays.asList(provider));
-			SecurityContextHolderStrategy securityContextHolderStrategy = this.bf
-				.getBeanProvider(SecurityContextHolderStrategy.class)
-				.getIfUnique(SecurityContextHolder::getContextHolderStrategy);
-			manager.setSecurityContextHolderStrategy(securityContextHolderStrategy);
 			if (this.observationRegistry.isNoop()) {
 				return manager;
 			}

+ 2 - 0
core/src/main/java/org/springframework/security/authentication/AbstractAuthenticationToken.java

@@ -16,6 +16,7 @@
 
 package org.springframework.security.authentication;
 
+import java.io.Serial;
 import java.security.Principal;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -43,6 +44,7 @@ import org.springframework.util.Assert;
  */
 public abstract class AbstractAuthenticationToken implements Authentication, CredentialsContainer {
 
+	@Serial
 	private static final long serialVersionUID = -3194696462184782834L;
 
 	private final Collection<GrantedAuthority> authorities;

+ 0 - 15
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<Authentication> authenticate(Authentication authentication) {
-		return ReactiveSecurityContextHolder.getContext().flatMap((context) -> {
-			Mono<Authentication> 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<Authentication> doAuthenticate(Authentication authentication) {
 		Flux<ReactiveAuthenticationManager> result = Flux.fromIterable(this.delegates);
 		Function<ReactiveAuthenticationManager, Mono<Authentication>> logging = (m) -> m.authenticate(authentication)
 			.doOnError(AuthenticationException.class, (ex) -> ex.setAuthenticationRequest(authentication))

+ 8 - 29
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<AuthenticationProvider> 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<AuthenticationProvider> 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);

+ 0 - 21
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);

+ 11 - 28
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<? extends GrantedAuthority>) 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<String> 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);

+ 6 - 0
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) {

+ 6 - 0
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();

+ 6 - 0
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) {

+ 4 - 0
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);

+ 14 - 0
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<Authentication> 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<Void> onAuthenticationSuccess(Authentication authentication, WebFilterExchange webFilterExchange) {
 		ServerWebExchange exchange = webFilterExchange.getExchange();
 		SecurityContextImpl securityContext = new SecurityContextImpl();

+ 1 - 0
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();