PasswordEncoder Bean for AuthenticationManagerBuilder
Issue: gh-4873
This commit is contained in:
parent
9afee9e4e2
commit
691bf2e11d
|
@ -27,6 +27,7 @@ import org.apache.commons.logging.LogFactory;
|
|||
import org.springframework.aop.framework.ProxyFactoryBean;
|
||||
import org.springframework.aop.target.LazyInitTargetSource;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
@ -36,9 +37,15 @@ import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
|||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer;
|
||||
import org.springframework.security.config.annotation.authentication.configurers.provisioning.JdbcUserDetailsManagerConfigurer;
|
||||
import org.springframework.security.config.annotation.authentication.configurers.userdetails.DaoAuthenticationConfigurer;
|
||||
import org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
|
@ -67,8 +74,9 @@ public class AuthenticationConfiguration {
|
|||
|
||||
@Bean
|
||||
public AuthenticationManagerBuilder authenticationManagerBuilder(
|
||||
ObjectPostProcessor<Object> objectPostProcessor) {
|
||||
return new AuthenticationManagerBuilder(objectPostProcessor);
|
||||
ObjectPostProcessor<Object> objectPostProcessor, ApplicationContext context) {
|
||||
LazyPasswordEncoder defaultPasswordEncoder = new LazyPasswordEncoder(context);
|
||||
return new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, defaultPasswordEncoder);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
@ -92,7 +100,7 @@ public class AuthenticationConfiguration {
|
|||
return this.authenticationManager;
|
||||
}
|
||||
AuthenticationManagerBuilder authBuilder = authenticationManagerBuilder(
|
||||
this.objectPostProcessor);
|
||||
this.objectPostProcessor, this.applicationContext);
|
||||
if (this.buildingAuthenticationManager.getAndSet(true)) {
|
||||
return new AuthenticationManagerDelegator(authBuilder);
|
||||
}
|
||||
|
@ -210,4 +218,85 @@ public class AuthenticationConfiguration {
|
|||
return "AuthenticationManagerDelegator [delegate=" + this.delegate + "]";
|
||||
}
|
||||
}
|
||||
|
||||
static class DefaultPasswordEncoderAuthenticationManagerBuilder extends AuthenticationManagerBuilder {
|
||||
private PasswordEncoder defaultPasswordEncoder;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
*
|
||||
* @param objectPostProcessor the {@link ObjectPostProcessor} instance to use.
|
||||
*/
|
||||
DefaultPasswordEncoderAuthenticationManagerBuilder(
|
||||
ObjectPostProcessor<Object> objectPostProcessor, PasswordEncoder defaultPasswordEncoder) {
|
||||
super(objectPostProcessor);
|
||||
this.defaultPasswordEncoder = defaultPasswordEncoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> inMemoryAuthentication()
|
||||
throws Exception {
|
||||
return super.inMemoryAuthentication()
|
||||
.passwordEncoder(this.defaultPasswordEncoder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder> jdbcAuthentication()
|
||||
throws Exception {
|
||||
return super.jdbcAuthentication()
|
||||
.passwordEncoder(this.defaultPasswordEncoder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends UserDetailsService> DaoAuthenticationConfigurer<AuthenticationManagerBuilder, T> userDetailsService(
|
||||
T userDetailsService) throws Exception {
|
||||
return super.userDetailsService(userDetailsService)
|
||||
.passwordEncoder(this.defaultPasswordEncoder);
|
||||
}
|
||||
}
|
||||
|
||||
static class LazyPasswordEncoder implements PasswordEncoder {
|
||||
private ApplicationContext applicationContext;
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
LazyPasswordEncoder(ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encode(CharSequence rawPassword) {
|
||||
return getPasswordEncoder().encode(rawPassword);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(CharSequence rawPassword,
|
||||
String encodedPassword) {
|
||||
return getPasswordEncoder().matches(rawPassword, encodedPassword);
|
||||
}
|
||||
|
||||
private PasswordEncoder getPasswordEncoder() {
|
||||
if (this.passwordEncoder != null) {
|
||||
return this.passwordEncoder;
|
||||
}
|
||||
PasswordEncoder passwordEncoder = getBeanOrNull(PasswordEncoder.class);
|
||||
if (passwordEncoder == null) {
|
||||
passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
|
||||
}
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
return passwordEncoder;
|
||||
}
|
||||
|
||||
private <T> T getBeanOrNull(Class<T> type) {
|
||||
try {
|
||||
return this.applicationContext.getBean(type);
|
||||
} catch(NoSuchBeanDefinitionException notFound) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getPasswordEncoder().toString();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -71,6 +71,7 @@ class InitializeUserDetailsBeanManagerConfigurer
|
|||
if (passwordEncoder != null) {
|
||||
provider.setPasswordEncoder(passwordEncoder);
|
||||
}
|
||||
provider.afterPropertiesSet();
|
||||
|
||||
auth.authenticationProvider(provider);
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.springframework.aop.framework.Advised;
|
|||
import org.springframework.aop.target.LazyInitTargetSource;
|
||||
import org.springframework.beans.FatalBeanException;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.annotation.Order;
|
||||
|
@ -42,6 +43,9 @@ import org.springframework.security.authentication.DefaultAuthenticationEventPub
|
|||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
|
||||
import org.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer;
|
||||
import org.springframework.security.config.annotation.authentication.configurers.provisioning.JdbcUserDetailsManagerConfigurer;
|
||||
import org.springframework.security.config.annotation.authentication.configurers.userdetails.DaoAuthenticationConfigurer;
|
||||
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||
|
@ -53,6 +57,8 @@ import org.springframework.security.core.AuthenticationException;
|
|||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
|
||||
import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;
|
||||
import org.springframework.util.Assert;
|
||||
|
@ -365,6 +371,19 @@ public abstract class WebSecurityConfigurerAdapter implements
|
|||
@Autowired
|
||||
public void setApplicationContext(ApplicationContext context) {
|
||||
this.context = context;
|
||||
|
||||
ObjectPostProcessor<Object> objectPostProcessor = context.getBean(ObjectPostProcessor.class);
|
||||
LazyPasswordEncoder passwordEncoder = new LazyPasswordEncoder(context);
|
||||
|
||||
authenticationBuilder = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, passwordEncoder);
|
||||
localConfigureAuthenticationBldr = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, passwordEncoder) {
|
||||
@Override
|
||||
public AuthenticationManagerBuilder eraseCredentials(boolean eraseCredentials) {
|
||||
authenticationBuilder.eraseCredentials(eraseCredentials);
|
||||
return super.eraseCredentials(eraseCredentials);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
|
@ -381,17 +400,6 @@ public abstract class WebSecurityConfigurerAdapter implements
|
|||
@Autowired
|
||||
public void setObjectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
|
||||
this.objectPostProcessor = objectPostProcessor;
|
||||
|
||||
authenticationBuilder = new AuthenticationManagerBuilder(objectPostProcessor);
|
||||
localConfigureAuthenticationBldr = new AuthenticationManagerBuilder(
|
||||
objectPostProcessor) {
|
||||
@Override
|
||||
public AuthenticationManagerBuilder eraseCredentials(boolean eraseCredentials) {
|
||||
authenticationBuilder.eraseCredentials(eraseCredentials);
|
||||
return super.eraseCredentials(eraseCredentials);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Autowired
|
||||
|
@ -530,4 +538,84 @@ public abstract class WebSecurityConfigurerAdapter implements
|
|||
}
|
||||
}
|
||||
|
||||
static class DefaultPasswordEncoderAuthenticationManagerBuilder extends AuthenticationManagerBuilder {
|
||||
private PasswordEncoder defaultPasswordEncoder;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
*
|
||||
* @param objectPostProcessor the {@link ObjectPostProcessor} instance to use.
|
||||
*/
|
||||
DefaultPasswordEncoderAuthenticationManagerBuilder(
|
||||
ObjectPostProcessor<Object> objectPostProcessor, PasswordEncoder defaultPasswordEncoder) {
|
||||
super(objectPostProcessor);
|
||||
this.defaultPasswordEncoder = defaultPasswordEncoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> inMemoryAuthentication()
|
||||
throws Exception {
|
||||
return super.inMemoryAuthentication()
|
||||
.passwordEncoder(this.defaultPasswordEncoder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder> jdbcAuthentication()
|
||||
throws Exception {
|
||||
return super.jdbcAuthentication()
|
||||
.passwordEncoder(this.defaultPasswordEncoder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends UserDetailsService> DaoAuthenticationConfigurer<AuthenticationManagerBuilder, T> userDetailsService(
|
||||
T userDetailsService) throws Exception {
|
||||
return super.userDetailsService(userDetailsService)
|
||||
.passwordEncoder(this.defaultPasswordEncoder);
|
||||
}
|
||||
}
|
||||
|
||||
static class LazyPasswordEncoder implements PasswordEncoder {
|
||||
private ApplicationContext applicationContext;
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
LazyPasswordEncoder(ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encode(CharSequence rawPassword) {
|
||||
return getPasswordEncoder().encode(rawPassword);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(CharSequence rawPassword,
|
||||
String encodedPassword) {
|
||||
return getPasswordEncoder().matches(rawPassword, encodedPassword);
|
||||
}
|
||||
|
||||
private PasswordEncoder getPasswordEncoder() {
|
||||
if (this.passwordEncoder != null) {
|
||||
return this.passwordEncoder;
|
||||
}
|
||||
PasswordEncoder passwordEncoder = getBeanOrNull(PasswordEncoder.class);
|
||||
if (passwordEncoder == null) {
|
||||
passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
|
||||
}
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
return passwordEncoder;
|
||||
}
|
||||
|
||||
private <T> T getBeanOrNull(Class<T> type) {
|
||||
try {
|
||||
return this.applicationContext.getBean(type);
|
||||
} catch(NoSuchBeanDefinitionException notFound) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getPasswordEncoder().toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package org.springframework.security.config.annotation.authentication
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration
|
||||
|
@ -37,6 +38,8 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
|
|||
import org.springframework.security.core.Authentication
|
||||
import org.springframework.security.core.userdetails.PasswordEncodedUser
|
||||
import org.springframework.security.core.userdetails.UserDetailsService
|
||||
import org.springframework.security.crypto.password.NoOpPasswordEncoder
|
||||
import org.springframework.security.crypto.password.PasswordEncoder
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||
|
||||
/**
|
||||
|
@ -71,6 +74,55 @@ class AuthenticationManagerBuilderTests extends BaseSpringSpec {
|
|||
am.eventPublisher == aep
|
||||
}
|
||||
|
||||
def "PasswordEncoder bean is used for Global"() {
|
||||
setup:
|
||||
loadConfig(PasswordEncoderGlobalConfig)
|
||||
when:
|
||||
Authentication auth = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("user","password"))
|
||||
then:
|
||||
auth.name == "user"
|
||||
auth.authorities*.authority == ['ROLE_USER']
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class PasswordEncoderGlobalConfig extends WebSecurityConfigurerAdapter {
|
||||
@Autowired
|
||||
void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER")
|
||||
}
|
||||
|
||||
@Bean
|
||||
PasswordEncoder passwordEncoder() {
|
||||
return NoOpPasswordEncoder.getInstance();
|
||||
}
|
||||
}
|
||||
|
||||
def "PasswordEncoder bean is used for protected"() {
|
||||
setup:
|
||||
loadConfig(PasswordEncoderConfig)
|
||||
when:
|
||||
Authentication auth = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("user","password"))
|
||||
then:
|
||||
auth.name == "user"
|
||||
auth.authorities*.authority == ['ROLE_USER']
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class PasswordEncoderConfig extends WebSecurityConfigurerAdapter {
|
||||
void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER")
|
||||
}
|
||||
|
||||
@Bean
|
||||
PasswordEncoder passwordEncoder() {
|
||||
return NoOpPasswordEncoder.getInstance();
|
||||
}
|
||||
}
|
||||
|
||||
def "authentication-manager support multiple DaoAuthenticationProvider's"() {
|
||||
setup:
|
||||
loadConfig(MultiAuthenticationProvidersConfig)
|
||||
|
|
|
@ -94,6 +94,7 @@ public class DaoAuthenticationProvider extends AbstractUserDetailsAuthentication
|
|||
|
||||
protected void doAfterPropertiesSet() throws Exception {
|
||||
Assert.notNull(this.userDetailsService, "A UserDetailsService must be set");
|
||||
this.userNotFoundEncodedPassword = this.passwordEncoder.encode(USER_NOT_FOUND_PASSWORD);
|
||||
}
|
||||
|
||||
protected final UserDetails retrieveUser(String username,
|
||||
|
@ -138,8 +139,6 @@ public class DaoAuthenticationProvider extends AbstractUserDetailsAuthentication
|
|||
*/
|
||||
public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
|
||||
Assert.notNull(passwordEncoder, "passwordEncoder cannot be null");
|
||||
|
||||
this.userNotFoundEncodedPassword = passwordEncoder.encode(USER_NOT_FOUND_PASSWORD);
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
}
|
||||
|
||||
|
|
|
@ -483,7 +483,7 @@ public class DaoAuthenticationProviderTests {
|
|||
|
||||
// SEC-2056
|
||||
@Test
|
||||
public void testUserNotFoundEncodesPassword() {
|
||||
public void testUserNotFoundEncodesPassword() throws Exception {
|
||||
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
|
||||
"missing", "koala");
|
||||
PasswordEncoder encoder = mock(PasswordEncoder.class);
|
||||
|
@ -492,6 +492,7 @@ public class DaoAuthenticationProviderTests {
|
|||
provider.setHideUserNotFoundExceptions(false);
|
||||
provider.setPasswordEncoder(encoder);
|
||||
provider.setUserDetailsService(new MockAuthenticationDaoUserrod());
|
||||
provider.afterPropertiesSet();
|
||||
try {
|
||||
provider.authenticate(token);
|
||||
fail("Expected Exception");
|
||||
|
|
Loading…
Reference in New Issue