Ensure the AuthenticationManager is created when needed

There was too much state really in the old implementation of
AuthenticationManagerConfiguration, and it was leading occasionally
to null pointers when method A assumed that method B had already
been called and it hadn't. This change manages to concentrate all the
references to an AuthenticationManagerBuilder into a single method
call, removoing the need for storing it at all.

Fixes gh-1556
This commit is contained in:
Dave Syer 2014-09-12 16:03:35 -05:00
parent 0950072b5e
commit 9902f98a3d
2 changed files with 50 additions and 29 deletions

View File

@ -26,6 +26,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.security.SecurityProperties.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -41,9 +42,15 @@ import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configurers.GlobalAuthenticationConfigurerAdapter;
import org.springframework.stereotype.Component;
/**
* Configuration for a Spring Security in-memory {@link AuthenticationManager}.
* Configuration for a Spring Security in-memory {@link AuthenticationManager}. Can be
* disabled by providing a bean of type AuthenticationManager. The value provided by this
* configuration will become the "global" authentication manager (from Spring Security),
* or the parent of the global instance. Thus it acts as a fallback when no others are
* provided, is used by method security if enabled, and as a parent authentication manager
* for "local" authentication managers in individual filter chains.
*
* @author Dave Syer
* @author Rob Winch
@ -53,8 +60,15 @@ import org.springframework.security.config.annotation.authentication.configurers
@ConditionalOnMissingBean(AuthenticationManager.class)
@Order(Ordered.LOWEST_PRECEDENCE - 3)
public class AuthenticationManagerConfiguration extends
GlobalAuthenticationConfigurerAdapter implements
ApplicationListener<ContextRefreshedEvent> {
GlobalAuthenticationConfigurerAdapter {
/*
* Yes, this class is a GlobalAuthenticationConfigurerAdapter, even though none of
* those methods are overridden: we want Spring Security to instantiate us early, so
* we can in turn force the SecurityPrequisites to be instantiated. This will prevent
* ordering issues between Spring Boot modules when they need to influence the default
* security configuration.
*/
private static Log logger = LogFactory
.getLog(AuthenticationManagerConfiguration.class);
@ -62,43 +76,48 @@ public class AuthenticationManagerConfiguration extends
@Autowired
private List<SecurityPrequisite> dependencies;
@Autowired
private ObjectPostProcessor<Object> objectPostProcessor;
@Autowired
private SecurityProperties security;
@Autowired
private AuthenticationEventPublisher authenticationEventPublisher;
private BootDefaultingAuthenticationConfigurerAdapter configurer = new BootDefaultingAuthenticationConfigurerAdapter();
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.apply(this.configurer);
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
this.configurer.configureParent(auth);
}
private ObjectPostProcessor<Object> objectPostProcessor;
@Bean
@Primary
public AuthenticationManager authenticationManager() {
AuthenticationManager manager = this.configurer.getAuthenticationManagerBuilder()
public AuthenticationManager authenticationManager(AuthenticationManagerBuilder auth)
throws Exception {
/*
* This AuthenticationManagerBuilder is for the global AuthenticationManager
*/
BootDefaultingAuthenticationConfigurerAdapter configurer = new BootDefaultingAuthenticationConfigurerAdapter();
configurer.init(auth);
configurer.configure(auth);
AuthenticationManager manager = configurer.getAuthenticationManagerBuilder()
.getOrBuild();
configurer.configureParent(auth);
return manager;
}
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
AuthenticationManager manager = this.configurer.getAuthenticationManagerBuilder()
.getOrBuild();
if (manager instanceof ProviderManager) {
((ProviderManager) manager)
.setAuthenticationEventPublisher(this.authenticationEventPublisher);
@Component
protected static class AuthenticationManagerConfigurationListener implements
ApplicationListener<ContextRefreshedEvent> {
@Autowired
private AuthenticationEventPublisher authenticationEventPublisher;
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ApplicationContext context = event.getApplicationContext();
if (context.getBeanNamesForType(AuthenticationManager.class).length == 0) {
return;
}
AuthenticationManager manager = context.getBean(AuthenticationManager.class);
if (manager instanceof ProviderManager) {
((ProviderManager) manager)
.setAuthenticationEventPublisher(this.authenticationEventPublisher);
}
}
}
/**
@ -163,7 +182,8 @@ public class AuthenticationManagerConfiguration extends
.roles(roles.toArray(new String[roles.size()])).and().and().build();
// Defer actually setting the parent on the AuthenticationManagerBuilder
// because it makes it "configured" and we are only in the init() phase here.
// because it makes it "configured" and we are only in the init() phase
// here.
}
}

View File

@ -101,6 +101,7 @@ public class SampleWebSecureApplication extends WebMvcConfigurerAdapter {
.password("admin").roles("ADMIN", "USER").and().withUser("user")
.password("user").roles("USER");
}
}
}