Fix fragile AuthenticationManagerConfiguration

Fixes gh-2474
pull/2475/merge
Rob Winch 10 years ago committed by Dave Syer
parent b8babd4eb4
commit e42fa79f7b

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2014 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -31,6 +31,7 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Primary;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationEventPublisher; import org.springframework.security.authentication.AuthenticationEventPublisher;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
@ -39,9 +40,8 @@ import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.config.annotation.ObjectPostProcessor; import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.SecurityConfigurer; import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 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.GlobalAuthenticationConfigurerAdapter; import org.springframework.security.config.annotation.authentication.configurers.GlobalAuthenticationConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
/** /**
@ -59,16 +59,7 @@ import org.springframework.stereotype.Component;
@ConditionalOnBean(ObjectPostProcessor.class) @ConditionalOnBean(ObjectPostProcessor.class)
@ConditionalOnMissingBean({ AuthenticationManager.class }) @ConditionalOnMissingBean({ AuthenticationManager.class })
@Order(0) @Order(0)
public class AuthenticationManagerConfiguration extends public class AuthenticationManagerConfiguration {
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 private static Log logger = LogFactory
.getLog(AuthenticationManagerConfiguration.class); .getLog(AuthenticationManagerConfiguration.class);
@ -76,35 +67,16 @@ public class AuthenticationManagerConfiguration extends
@Autowired @Autowired
private List<SecurityPrerequisite> dependencies; private List<SecurityPrerequisite> dependencies;
@Autowired
private SecurityProperties security;
@Autowired
private ObjectPostProcessor<Object> objectPostProcessor;
@Bean @Bean
@Primary @Primary
public AuthenticationManager authenticationManager(AuthenticationManagerBuilder auth, public AuthenticationManager authenticationManager(AuthenticationConfiguration auth) throws Exception {
ApplicationContext context) throws Exception { return auth.getAuthenticationManager();
if (isAuthenticationManagerAlreadyConfigured(context)) {
return new LazyAuthenticationManager(auth);
}
/*
* This AuthenticationManagerBuilder is for the global AuthenticationManager
*/
BootDefaultingAuthenticationConfigurerAdapter configurer = new BootDefaultingAuthenticationConfigurerAdapter();
configurer.configure(auth);
AuthenticationManager manager = configurer.getAuthenticationManagerBuilder()
.getOrBuild();
configurer.configureParent(auth);
return manager;
} }
private boolean isAuthenticationManagerAlreadyConfigured(ApplicationContext context) { @Bean
return context.getBeanNamesForType(GlobalAuthenticationConfigurerAdapter.class).length > 2; public static BootDefaultingAuthenticationConfigurerAdapter bootDefaultingAuthenticationConfigurerAdapter(SecurityProperties security,
List<SecurityPrerequisite> dependencies) {
return new BootDefaultingAuthenticationConfigurerAdapter(security);
} }
@Component @Component
@ -128,10 +100,6 @@ public class AuthenticationManagerConfiguration extends
((ProviderManager) manager) ((ProviderManager) manager)
.setAuthenticationEventPublisher(this.authenticationEventPublisher); .setAuthenticationEventPublisher(this.authenticationEventPublisher);
} }
else if (manager instanceof LazyAuthenticationManager) {
((LazyAuthenticationManager) manager)
.setAuthenticationEventPublisher(this.authenticationEventPublisher);
}
} }
} }
@ -157,74 +125,31 @@ public class AuthenticationManagerConfiguration extends
* methods are invoked before configure, which cannot be guaranteed at this point.</li> * methods are invoked before configure, which cannot be guaranteed at this point.</li>
* </ul> * </ul>
*/ */
private class BootDefaultingAuthenticationConfigurerAdapter { @Order(Ordered.LOWEST_PRECEDENCE - 100)
private static class BootDefaultingAuthenticationConfigurerAdapter extends GlobalAuthenticationConfigurerAdapter {
private AuthenticationManagerBuilder defaultAuth; private final SecurityProperties security;
private AuthenticationManager parent;
public void configureParent(AuthenticationManagerBuilder auth) { @Autowired
if (!auth.isConfigured() && this.parent != null) { public BootDefaultingAuthenticationConfigurerAdapter(SecurityProperties security) {
auth.parentAuthenticationManager(this.parent); this.security = security;
}
}
public AuthenticationManagerBuilder getAuthenticationManagerBuilder() {
return this.defaultAuth;
} }
public void configure(AuthenticationManagerBuilder auth) throws Exception { public void init(AuthenticationManagerBuilder auth) throws Exception {
if (auth.isConfigured()) { if (auth.isConfigured()) {
this.defaultAuth = auth;
return; return;
} }
User user = AuthenticationManagerConfiguration.this.security.getUser();
User user = this.security.getUser();
if (user.isDefaultPassword()) { if (user.isDefaultPassword()) {
logger.info("\n\nUsing default security password: " + user.getPassword() logger.info("\n\nUsing default security password: " + user.getPassword()
+ "\n"); + "\n");
} }
this.defaultAuth = new AuthenticationManagerBuilder(
AuthenticationManagerConfiguration.this.objectPostProcessor);
Set<String> roles = new LinkedHashSet<String>(user.getRole()); Set<String> roles = new LinkedHashSet<String>(user.getRole());
this.parent = this.defaultAuth.inMemoryAuthentication() auth
.withUser(user.getName()).password(user.getPassword()) .inMemoryAuthentication()
.roles(roles.toArray(new String[roles.size()])).and().and().build(); .withUser(user.getName())
// Defer actually setting the parent on the AuthenticationManagerBuilder .password(user.getPassword())
// because it makes it "configured" and we are only in the init() phase .roles(roles.toArray(new String[roles.size()]));
// here.
}
}
private static class LazyAuthenticationManager implements AuthenticationManager {
private AuthenticationManagerBuilder builder;
private AuthenticationManager authenticationManager;
private AuthenticationEventPublisher authenticationEventPublisher;
public LazyAuthenticationManager(AuthenticationManagerBuilder builder) {
this.builder = builder;
} }
public void setAuthenticationEventPublisher(
AuthenticationEventPublisher authenticationEventPublisher) {
this.authenticationEventPublisher = authenticationEventPublisher;
} }
@Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
if (this.authenticationManager == null) {
this.authenticationManager = this.builder.getOrBuild();
if (this.authenticationManager instanceof ProviderManager) {
((ProviderManager) this.authenticationManager)
.setAuthenticationEventPublisher(this.authenticationEventPublisher);
}
}
return this.authenticationManager.authenticate(authentication);
}
}
} }
Loading…
Cancel
Save