mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-10-23 10:48:51 +00:00
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
This commit is contained in:
parent
a0fe6a5fee
commit
3f774548d2
@ -18,14 +18,10 @@ package org.springframework.security.config.annotation.authentication.builders;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
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.AuthenticationEventPublisher;
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
import org.springframework.security.authentication.AuthenticationProvider;
|
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.DaoAuthenticationConfigurer;
|
||||||
import org.springframework.security.config.annotation.authentication.configurers.userdetails.UserDetailsAwareConfigurer;
|
import org.springframework.security.config.annotation.authentication.configurers.userdetails.UserDetailsAwareConfigurer;
|
||||||
import org.springframework.security.core.Authentication;
|
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.security.core.userdetails.UserDetailsService;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
@ -241,10 +235,6 @@ public class AuthenticationManagerBuilder
|
|||||||
if (this.eventPublisher != null) {
|
if (this.eventPublisher != null) {
|
||||||
providerManager.setAuthenticationEventPublisher(this.eventPublisher);
|
providerManager.setAuthenticationEventPublisher(this.eventPublisher);
|
||||||
}
|
}
|
||||||
SecurityContextHolderStrategy securityContextHolderStrategy = getBeanProvider(
|
|
||||||
SecurityContextHolderStrategy.class)
|
|
||||||
.getIfUnique(SecurityContextHolder::getContextHolderStrategy);
|
|
||||||
providerManager.setSecurityContextHolderStrategy(securityContextHolderStrategy);
|
|
||||||
providerManager = postProcess(providerManager);
|
providerManager = postProcess(providerManager);
|
||||||
return providerManager;
|
return providerManager;
|
||||||
}
|
}
|
||||||
@ -293,24 +283,4 @@ public class AuthenticationManagerBuilder
|
|||||||
return configurer;
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,6 @@ import org.apache.commons.logging.LogFactory;
|
|||||||
|
|
||||||
import org.springframework.aop.framework.ProxyFactoryBean;
|
import org.springframework.aop.framework.ProxyFactoryBean;
|
||||||
import org.springframework.aop.target.LazyInitTargetSource;
|
import org.springframework.aop.target.LazyInitTargetSource;
|
||||||
import org.springframework.beans.factory.BeanFactory;
|
|
||||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
@ -84,7 +83,6 @@ public class AuthenticationConfiguration {
|
|||||||
AuthenticationEventPublisher authenticationEventPublisher = getAuthenticationEventPublisher(context);
|
AuthenticationEventPublisher authenticationEventPublisher = getAuthenticationEventPublisher(context);
|
||||||
DefaultPasswordEncoderAuthenticationManagerBuilder result = new DefaultPasswordEncoderAuthenticationManagerBuilder(
|
DefaultPasswordEncoderAuthenticationManagerBuilder result = new DefaultPasswordEncoderAuthenticationManagerBuilder(
|
||||||
objectPostProcessor, defaultPasswordEncoder);
|
objectPostProcessor, defaultPasswordEncoder);
|
||||||
result.setSharedObject(BeanFactory.class, this.applicationContext);
|
|
||||||
if (authenticationEventPublisher != null) {
|
if (authenticationEventPublisher != null) {
|
||||||
result.authenticationEventPublisher(authenticationEventPublisher);
|
result.authenticationEventPublisher(authenticationEventPublisher);
|
||||||
}
|
}
|
||||||
|
@ -318,7 +318,6 @@ public class GlobalMethodSecurityConfiguration implements ImportAware, SmartInit
|
|||||||
.postProcess(new DefaultAuthenticationEventPublisher());
|
.postProcess(new DefaultAuthenticationEventPublisher());
|
||||||
this.auth = new AuthenticationManagerBuilder(this.objectPostProcessor);
|
this.auth = new AuthenticationManagerBuilder(this.objectPostProcessor);
|
||||||
this.auth.authenticationEventPublisher(eventPublisher);
|
this.auth.authenticationEventPublisher(eventPublisher);
|
||||||
this.auth.setSharedObject(BeanFactory.class, this.context);
|
|
||||||
configure(this.auth);
|
configure(this.auth);
|
||||||
this.authenticationManager = (this.disableAuthenticationRegistry)
|
this.authenticationManager = (this.disableAuthenticationRegistry)
|
||||||
? getAuthenticationConfiguration().getAuthenticationManager() : this.auth.build();
|
? getAuthenticationConfiguration().getAuthenticationManager() : this.auth.build();
|
||||||
|
@ -21,7 +21,6 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.springframework.beans.factory.BeanFactory;
|
|
||||||
import org.springframework.beans.factory.ObjectProvider;
|
import org.springframework.beans.factory.ObjectProvider;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
@ -117,7 +116,6 @@ class HttpSecurityConfiguration {
|
|||||||
LazyPasswordEncoder passwordEncoder = new LazyPasswordEncoder(this.context);
|
LazyPasswordEncoder passwordEncoder = new LazyPasswordEncoder(this.context);
|
||||||
AuthenticationManagerBuilder authenticationBuilder = new DefaultPasswordEncoderAuthenticationManagerBuilder(
|
AuthenticationManagerBuilder authenticationBuilder = new DefaultPasswordEncoderAuthenticationManagerBuilder(
|
||||||
this.objectPostProcessor, passwordEncoder);
|
this.objectPostProcessor, passwordEncoder);
|
||||||
authenticationBuilder.setSharedObject(BeanFactory.class, this.context);
|
|
||||||
authenticationBuilder.parentAuthenticationManager(authenticationManager());
|
authenticationBuilder.parentAuthenticationManager(authenticationManager());
|
||||||
authenticationBuilder.authenticationEventPublisher(getAuthenticationEventPublisher());
|
authenticationBuilder.authenticationEventPublisher(getAuthenticationEventPublisher());
|
||||||
HttpSecurity http = new HttpSecurity(this.objectPostProcessor, authenticationBuilder, createSharedObjects());
|
HttpSecurity http = new HttpSecurity(this.objectPostProcessor, authenticationBuilder, createSharedObjects());
|
||||||
|
@ -162,10 +162,8 @@ public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
|
|||||||
WebAuthnRelyingPartyOperations rpOperations = webAuthnRelyingPartyOperations(userEntities, userCredentials);
|
WebAuthnRelyingPartyOperations rpOperations = webAuthnRelyingPartyOperations(userEntities, userCredentials);
|
||||||
PublicKeyCredentialCreationOptionsRepository creationOptionsRepository = creationOptionsRepository();
|
PublicKeyCredentialCreationOptionsRepository creationOptionsRepository = creationOptionsRepository();
|
||||||
WebAuthnAuthenticationFilter webAuthnAuthnFilter = new WebAuthnAuthenticationFilter();
|
WebAuthnAuthenticationFilter webAuthnAuthnFilter = new WebAuthnAuthenticationFilter();
|
||||||
ProviderManager manager = new ProviderManager(
|
webAuthnAuthnFilter.setAuthenticationManager(
|
||||||
new WebAuthnAuthenticationProvider(rpOperations, userDetailsService));
|
new ProviderManager(new WebAuthnAuthenticationProvider(rpOperations, userDetailsService)));
|
||||||
manager.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
|
|
||||||
webAuthnAuthnFilter.setAuthenticationManager(manager);
|
|
||||||
WebAuthnRegistrationFilter webAuthnRegistrationFilter = new WebAuthnRegistrationFilter(userCredentials,
|
WebAuthnRegistrationFilter webAuthnRegistrationFilter = new WebAuthnRegistrationFilter(userCredentials,
|
||||||
rpOperations);
|
rpOperations);
|
||||||
PublicKeyCredentialCreationOptionsFilter creationOptionsFilter = new PublicKeyCredentialCreationOptionsFilter(
|
PublicKeyCredentialCreationOptionsFilter creationOptionsFilter = new PublicKeyCredentialCreationOptionsFilter(
|
||||||
|
@ -30,8 +30,6 @@ import org.springframework.security.authentication.ObservationAuthenticationMana
|
|||||||
import org.springframework.security.authentication.ProviderManager;
|
import org.springframework.security.authentication.ProviderManager;
|
||||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||||
import org.springframework.security.config.BeanIds;
|
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.core.userdetails.UserDetailsService;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
|
||||||
@ -74,10 +72,6 @@ public class AuthenticationManagerFactoryBean implements FactoryBean<Authenticat
|
|||||||
}
|
}
|
||||||
provider.afterPropertiesSet();
|
provider.afterPropertiesSet();
|
||||||
ProviderManager manager = new ProviderManager(Arrays.asList(provider));
|
ProviderManager manager = new ProviderManager(Arrays.asList(provider));
|
||||||
SecurityContextHolderStrategy securityContextHolderStrategy = this.bf
|
|
||||||
.getBeanProvider(SecurityContextHolderStrategy.class)
|
|
||||||
.getIfUnique(SecurityContextHolder::getContextHolderStrategy);
|
|
||||||
manager.setSecurityContextHolderStrategy(securityContextHolderStrategy);
|
|
||||||
if (this.observationRegistry.isNoop()) {
|
if (this.observationRegistry.isNoop()) {
|
||||||
return manager;
|
return manager;
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.springframework.security.authentication;
|
package org.springframework.security.authentication;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@ -43,6 +44,7 @@ import org.springframework.util.Assert;
|
|||||||
*/
|
*/
|
||||||
public abstract class AbstractAuthenticationToken implements Authentication, CredentialsContainer {
|
public abstract class AbstractAuthenticationToken implements Authentication, CredentialsContainer {
|
||||||
|
|
||||||
|
@Serial
|
||||||
private static final long serialVersionUID = -3194696462184782834L;
|
private static final long serialVersionUID = -3194696462184782834L;
|
||||||
|
|
||||||
private final Collection<GrantedAuthority> authorities;
|
private final Collection<GrantedAuthority> authorities;
|
||||||
|
@ -27,7 +27,6 @@ import reactor.core.publisher.Mono;
|
|||||||
|
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
|
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -58,20 +57,6 @@ public class DelegatingReactiveAuthenticationManager implements ReactiveAuthenti
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<Authentication> authenticate(Authentication authentication) {
|
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);
|
Flux<ReactiveAuthenticationManager> result = Flux.fromIterable(this.delegates);
|
||||||
Function<ReactiveAuthenticationManager, Mono<Authentication>> logging = (m) -> m.authenticate(authentication)
|
Function<ReactiveAuthenticationManager, Mono<Authentication>> logging = (m) -> m.authenticate(authentication)
|
||||||
.doOnError(AuthenticationException.class, (ex) -> ex.setAuthenticationRequest(authentication))
|
.doOnError(AuthenticationException.class, (ex) -> ex.setAuthenticationRequest(authentication))
|
||||||
|
@ -33,8 +33,6 @@ import org.springframework.security.core.Authentication;
|
|||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
import org.springframework.security.core.CredentialsContainer;
|
import org.springframework.security.core.CredentialsContainer;
|
||||||
import org.springframework.security.core.SpringSecurityMessageSource;
|
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.Assert;
|
||||||
import org.springframework.util.CollectionUtils;
|
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 static final Log logger = LogFactory.getLog(ProviderManager.class);
|
||||||
|
|
||||||
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
|
|
||||||
.getContextHolderStrategy();
|
|
||||||
|
|
||||||
private AuthenticationEventPublisher eventPublisher = new NullEventPublisher();
|
private AuthenticationEventPublisher eventPublisher = new NullEventPublisher();
|
||||||
|
|
||||||
private List<AuthenticationProvider> providers = Collections.emptyList();
|
private List<AuthenticationProvider> providers = Collections.emptyList();
|
||||||
@ -187,7 +182,7 @@ public class ProviderManager implements AuthenticationManager, MessageSourceAwar
|
|||||||
try {
|
try {
|
||||||
result = provider.authenticate(authentication);
|
result = provider.authenticate(authentication);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
copyDetails(authentication, result);
|
result = copyDetails(authentication, result);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -214,7 +209,6 @@ public class ProviderManager implements AuthenticationManager, MessageSourceAwar
|
|||||||
lastException = ex;
|
lastException = ex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result = applyPreviousAuthentication(result);
|
|
||||||
if (result == null && this.parent != null) {
|
if (result == null && this.parent != null) {
|
||||||
// Allow the parent to try.
|
// Allow the parent to try.
|
||||||
try {
|
try {
|
||||||
@ -271,20 +265,6 @@ public class ProviderManager implements AuthenticationManager, MessageSourceAwar
|
|||||||
throw lastException;
|
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")
|
@SuppressWarnings("deprecation")
|
||||||
private void prepareException(AuthenticationException ex, Authentication auth) {
|
private void prepareException(AuthenticationException ex, Authentication auth) {
|
||||||
ex.setAuthenticationRequest(auth);
|
ex.setAuthenticationRequest(auth);
|
||||||
@ -297,21 +277,20 @@ public class ProviderManager implements AuthenticationManager, MessageSourceAwar
|
|||||||
* @param source source authentication
|
* @param source source authentication
|
||||||
* @param dest the destination authentication object
|
* @param dest the destination authentication object
|
||||||
*/
|
*/
|
||||||
private void copyDetails(Authentication source, Authentication dest) {
|
private Authentication copyDetails(Authentication source, Authentication dest) {
|
||||||
if ((dest instanceof AbstractAuthenticationToken token) && (dest.getDetails() == null)) {
|
if (source.getDetails() == null) {
|
||||||
token.setDetails(source.getDetails());
|
return dest;
|
||||||
}
|
}
|
||||||
|
if (dest.getDetails() != null) {
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
return dest.toBuilder().details(source.getDetails()).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<AuthenticationProvider> getProviders() {
|
public List<AuthenticationProvider> getProviders() {
|
||||||
return this.providers;
|
return this.providers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
|
|
||||||
Assert.notNull(securityContextHolderStrategy, "securityContextHolderStrategy cannot be null");
|
|
||||||
this.securityContextHolderStrategy = securityContextHolderStrategy;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setMessageSource(MessageSource messageSource) {
|
public void setMessageSource(MessageSource messageSource) {
|
||||||
this.messages = new MessageSourceAccessor(messageSource);
|
this.messages = new MessageSourceAccessor(messageSource);
|
||||||
|
@ -27,13 +27,10 @@ import reactor.test.StepVerifier;
|
|||||||
|
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
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.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.BDDMockito.given;
|
import static org.mockito.BDDMockito.given;
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
@ -121,24 +118,6 @@ public class DelegatingReactiveAuthenticationManagerTests {
|
|||||||
assertThat(expected.getAuthenticationRequest()).isEqualTo(this.authentication);
|
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() {
|
private DelegatingReactiveAuthenticationManager managerWithContinueOnError() {
|
||||||
DelegatingReactiveAuthenticationManager manager = new DelegatingReactiveAuthenticationManager(this.delegate1,
|
DelegatingReactiveAuthenticationManager manager = new DelegatingReactiveAuthenticationManager(this.delegate1,
|
||||||
this.delegate2);
|
this.delegate2);
|
||||||
|
@ -18,17 +18,15 @@ package org.springframework.security.authentication;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import org.springframework.context.MessageSource;
|
import org.springframework.context.MessageSource;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
import org.springframework.security.core.authority.AuthorityUtils;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
|
||||||
import org.springframework.security.core.context.SecurityContextImpl;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
@ -51,7 +49,7 @@ public class ProviderManagerTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void authenticationFailsWithUnsupportedToken() {
|
void authenticationFailsWithUnsupportedToken() {
|
||||||
Authentication token = new AbstractAuthenticationToken(null) {
|
Authentication token = new AbstractAuthenticationToken((Collection<? extends GrantedAuthority>) null) {
|
||||||
@Override
|
@Override
|
||||||
public Object getCredentials() {
|
public Object getCredentials() {
|
||||||
return "";
|
return "";
|
||||||
@ -82,24 +80,24 @@ public class ProviderManagerTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void authenticationSucceedsWithSupportedTokenAndReturnsExpectedObject() {
|
void authenticationSucceedsWithSupportedTokenAndReturnsExpectedObject() {
|
||||||
Authentication a = mock(Authentication.class);
|
Authentication a = new TestingAuthenticationToken("user", "pass", "FACTOR");
|
||||||
ProviderManager mgr = new ProviderManager(createProviderWhichReturns(a));
|
ProviderManager mgr = new ProviderManager(createProviderWhichReturns(a));
|
||||||
AuthenticationEventPublisher publisher = mock(AuthenticationEventPublisher.class);
|
AuthenticationEventPublisher publisher = mock(AuthenticationEventPublisher.class);
|
||||||
mgr.setAuthenticationEventPublisher(publisher);
|
mgr.setAuthenticationEventPublisher(publisher);
|
||||||
Authentication result = mgr.authenticate(a);
|
Authentication result = mgr.authenticate(a);
|
||||||
assertThat(result).isEqualTo(a);
|
assertThat(result.getPrincipal()).isEqualTo(a.getPrincipal());
|
||||||
verify(publisher).publishAuthenticationSuccess(result);
|
verify(publisher).publishAuthenticationSuccess(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void authenticationSucceedsWhenFirstProviderReturnsNullButSecondAuthenticates() {
|
void authenticationSucceedsWhenFirstProviderReturnsNullButSecondAuthenticates() {
|
||||||
Authentication a = mock(Authentication.class);
|
Authentication a = new TestingAuthenticationToken("user", "pass", "FACTOR");
|
||||||
ProviderManager mgr = new ProviderManager(
|
ProviderManager mgr = new ProviderManager(
|
||||||
Arrays.asList(createProviderWhichReturns(null), createProviderWhichReturns(a)));
|
Arrays.asList(createProviderWhichReturns(null), createProviderWhichReturns(a)));
|
||||||
AuthenticationEventPublisher publisher = mock(AuthenticationEventPublisher.class);
|
AuthenticationEventPublisher publisher = mock(AuthenticationEventPublisher.class);
|
||||||
mgr.setAuthenticationEventPublisher(publisher);
|
mgr.setAuthenticationEventPublisher(publisher);
|
||||||
Authentication result = mgr.authenticate(a);
|
Authentication result = mgr.authenticate(a);
|
||||||
assertThat(result).isSameAs(a);
|
assertThat(result.getPrincipal()).isEqualTo(a.getPrincipal());
|
||||||
verify(publisher).publishAuthenticationSuccess(result);
|
verify(publisher).publishAuthenticationSuccess(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,11 +164,12 @@ public class ProviderManagerTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void authenticationExceptionIsIgnoredIfLaterProviderAuthenticates() {
|
void authenticationExceptionIsIgnoredIfLaterProviderAuthenticates() {
|
||||||
Authentication authReq = mock(Authentication.class);
|
Authentication result = new TestingAuthenticationToken("user", "pass", "FACTOR");
|
||||||
ProviderManager mgr = new ProviderManager(
|
ProviderManager mgr = new ProviderManager(
|
||||||
createProviderWhichThrows(new BadCredentialsException("", new Throwable())),
|
createProviderWhichThrows(new BadCredentialsException("", new Throwable())),
|
||||||
createProviderWhichReturns(authReq));
|
createProviderWhichReturns(result));
|
||||||
assertThat(mgr.authenticate(mock(Authentication.class))).isSameAs(authReq);
|
Authentication request = new TestingAuthenticationToken("user", "pass");
|
||||||
|
assertThat(mgr.authenticate(request).getPrincipal()).isEqualTo(result.getPrincipal());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -314,22 +313,6 @@ public class ProviderManagerTests {
|
|||||||
verifyNoMoreInteractions(publisher); // Child should not publish (duplicate event)
|
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) {
|
private AuthenticationProvider createProviderWhichThrows(final AuthenticationException ex) {
|
||||||
AuthenticationProvider provider = mock(AuthenticationProvider.class);
|
AuthenticationProvider provider = mock(AuthenticationProvider.class);
|
||||||
given(provider.supports(any(Class.class))).willReturn(true);
|
given(provider.supports(any(Class.class))).willReturn(true);
|
||||||
|
@ -248,6 +248,12 @@ public abstract class AbstractAuthenticationProcessingFilter extends GenericFilt
|
|||||||
// return immediately as subclass has indicated that it hasn't completed
|
// return immediately as subclass has indicated that it hasn't completed
|
||||||
return;
|
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);
|
this.sessionStrategy.onAuthentication(authenticationResult, request, response);
|
||||||
// Authentication success
|
// Authentication success
|
||||||
if (this.continueChainBeforeSuccessfulAuthentication) {
|
if (this.continueChainBeforeSuccessfulAuthentication) {
|
||||||
|
@ -184,6 +184,12 @@ public class AuthenticationFilter extends OncePerRequestFilter {
|
|||||||
filterChain.doFilter(request, response);
|
filterChain.doFilter(request, response);
|
||||||
return;
|
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);
|
HttpSession session = request.getSession(false);
|
||||||
if (session != null) {
|
if (session != null) {
|
||||||
request.changeSessionId();
|
request.changeSessionId();
|
||||||
|
@ -204,6 +204,12 @@ public abstract class AbstractPreAuthenticatedProcessingFilter extends GenericFi
|
|||||||
principal, credentials);
|
principal, credentials);
|
||||||
authenticationRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
|
authenticationRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
|
||||||
Authentication authenticationResult = this.authenticationManager.authenticate(authenticationRequest);
|
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);
|
successfulAuthentication(request, response, authenticationResult);
|
||||||
}
|
}
|
||||||
catch (AuthenticationException ex) {
|
catch (AuthenticationException ex) {
|
||||||
|
@ -186,6 +186,10 @@ public class BasicAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
this.logger.trace(LogMessage.format("Found username '%s' in Basic Authorization header", username));
|
this.logger.trace(LogMessage.format("Found username '%s' in Basic Authorization header", username));
|
||||||
if (authenticationIsRequired(username)) {
|
if (authenticationIsRequired(username)) {
|
||||||
Authentication authResult = this.authenticationManager.authenticate(authRequest);
|
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();
|
SecurityContext context = this.securityContextHolderStrategy.createEmptyContext();
|
||||||
context.setAuthentication(authResult);
|
context.setAuthentication(authResult);
|
||||||
this.securityContextHolderStrategy.setContext(context);
|
this.securityContextHolderStrategy.setContext(context);
|
||||||
|
@ -122,12 +122,26 @@ public class AuthenticationWebFilter implements WebFilter {
|
|||||||
.flatMap((authenticationManager) -> authenticationManager.authenticate(token))
|
.flatMap((authenticationManager) -> authenticationManager.authenticate(token))
|
||||||
.switchIfEmpty(Mono
|
.switchIfEmpty(Mono
|
||||||
.defer(() -> Mono.error(new IllegalStateException("No provider found for " + token.getClass()))))
|
.defer(() -> Mono.error(new IllegalStateException("No provider found for " + token.getClass()))))
|
||||||
|
.flatMap(this::applyCurrentAuthenication)
|
||||||
.flatMap(
|
.flatMap(
|
||||||
(authentication) -> onAuthenticationSuccess(authentication, new WebFilterExchange(exchange, chain)))
|
(authentication) -> onAuthenticationSuccess(authentication, new WebFilterExchange(exchange, chain)))
|
||||||
.doOnError(AuthenticationException.class,
|
.doOnError(AuthenticationException.class,
|
||||||
(ex) -> logger.debug(LogMessage.format("Authentication failed: %s", ex.getMessage()), ex));
|
(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) {
|
protected Mono<Void> onAuthenticationSuccess(Authentication authentication, WebFilterExchange webFilterExchange) {
|
||||||
ServerWebExchange exchange = webFilterExchange.getExchange();
|
ServerWebExchange exchange = webFilterExchange.getExchange();
|
||||||
SecurityContextImpl securityContext = new SecurityContextImpl();
|
SecurityContextImpl securityContext = new SecurityContextImpl();
|
||||||
|
@ -144,6 +144,7 @@ public class AuthenticationFilterTests {
|
|||||||
this.authenticationConverter);
|
this.authenticationConverter);
|
||||||
SecurityContextHolderStrategy strategy = mock(SecurityContextHolderStrategy.class);
|
SecurityContextHolderStrategy strategy = mock(SecurityContextHolderStrategy.class);
|
||||||
given(strategy.createEmptyContext()).willReturn(new SecurityContextImpl());
|
given(strategy.createEmptyContext()).willReturn(new SecurityContextImpl());
|
||||||
|
given(strategy.getContext()).willReturn(new SecurityContextImpl());
|
||||||
filter.setSecurityContextHolderStrategy(strategy);
|
filter.setSecurityContextHolderStrategy(strategy);
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/");
|
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/");
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user