Register an AuthenticationManager in security autoconfig

This is quite a big step, but I think it helps a lot. Since Spring
Boot always creates an AuthenticationManager if it doesn't find one
already registered, it makes sense to also make it into a @Bean.
Spring Security does not register its AuthenticationManager by
default though, so we have to do that for it if the user has created
one with an @Autowired AuthenticationManagerBuilder, but not registered
it as a @Bean.

Having the @Bean (marked @Primary to prevent issues with @Autowired)
makes it easier to reason about what Spring Boot has done for you, and
easier to default in simple use cases to the boot-created
AuthenticationManager. For example, if I want an OAuth2 Authorization
Server with password grant, it makes total sense for the
AuthenticationManager for users to be the same as the @Primary one.
Now it is easy to set that up (just @Autowire it).
This commit is contained in:
Dave Syer
2014-03-13 11:51:38 +00:00
parent 85a56a79e4
commit 3d43771136
3 changed files with 55 additions and 24 deletions

View File

@@ -40,10 +40,19 @@ public abstract class SpringBootCondition implements Condition {
@Override
public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String classOrMethodName = getClassOrMethodName(metadata);
ConditionOutcome outcome = getMatchOutcome(context, metadata);
logOutcome(classOrMethodName, outcome);
recordEvaluation(context, classOrMethodName, outcome);
return outcome.isMatch();
try {
ConditionOutcome outcome = getMatchOutcome(context, metadata);
logOutcome(classOrMethodName, outcome);
recordEvaluation(context, classOrMethodName, outcome);
return outcome.isMatch();
}
catch (NoClassDefFoundError e) {
throw new IllegalStateException(
"Could not evaluate condition owing to internal class not found. "
+ "This can happen if you are @ComponentScanning a springframework package "
+ "(e.g. if you put a @ComponentScan in the default package by mistake)",
e);
}
}
private static String getClassOrMethodName(AnnotatedTypeMetadata metadata) {

View File

@@ -26,7 +26,12 @@ 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.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
@@ -60,9 +65,25 @@ public class AuthenticationManagerConfiguration extends
@Autowired
private SecurityProperties security;
private BootDefaultingAuthenticationConfigurerAdapter configurer = new BootDefaultingAuthenticationConfigurerAdapter();
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.apply(new BootDefaultingAuthenticationConfigurerAdapter());
auth.apply(this.configurer);
}
@Bean
// avoid issues with scopedTarget (SPR-11548)
@Primary
public AuthenticationManager authenticationManager() {
return lazyAuthenticationManager();
}
@Bean
@Lazy
@Scope(proxyMode = ScopedProxyMode.INTERFACES)
protected AuthenticationManager lazyAuthenticationManager() {
return this.configurer.getAuthenticationManagerBuilder().getOrBuild();
}
/**
@@ -92,9 +113,16 @@ public class AuthenticationManagerConfiguration extends
private class BootDefaultingAuthenticationConfigurerAdapter extends
GlobalAuthenticationConfigurerAdapter {
private AuthenticationManagerBuilder defaultAuth;
public AuthenticationManagerBuilder getAuthenticationManagerBuilder() {
return this.defaultAuth;
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
if (auth.isConfigured()) {
this.defaultAuth = auth;
return;
}
@@ -104,12 +132,12 @@ public class AuthenticationManagerConfiguration extends
+ user.getPassword() + "\n\n");
}
AuthenticationManagerBuilder defaultAuth = new AuthenticationManagerBuilder(
this.defaultAuth = new AuthenticationManagerBuilder(
AuthenticationManagerConfiguration.this.objectPostProcessor);
Set<String> roles = new LinkedHashSet<String>(user.getRole());
AuthenticationManager parent = defaultAuth.inMemoryAuthentication()
AuthenticationManager parent = this.defaultAuth.inMemoryAuthentication()
.withUser(user.getName()).password(user.getPassword())
.roles(roles.toArray(new String[roles.size()])).and().and().build();

View File

@@ -19,15 +19,11 @@ package org.springframework.boot.autoconfigure.security;
import java.util.List;
import org.junit.Test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.test.City;
import org.springframework.boot.context.event.ApplicationPreparedEvent;
import org.springframework.boot.logging.LoggingApplicationListener;
import org.springframework.boot.test.EnvironmentTestUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -60,7 +56,7 @@ public class SecurityAutoConfigurationTests {
this.context.setServletContext(new MockServletContext());
this.context.register(SecurityAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
debugRefresh(this.context);
this.context.refresh();
assertNotNull(this.context.getBean(AuthenticationManagerBuilder.class));
// 4 for static resources and one for the rest
List<SecurityFilterChain> filterChains = this.context.getBean(
@@ -93,6 +89,16 @@ public class SecurityAutoConfigurationTests {
assertEquals(0, this.context.getBeanNamesForType(FilterChainProxy.class).length);
}
@Test
public void testAuthenticationManagerCreated() throws Exception {
this.context = new AnnotationConfigWebApplicationContext();
this.context.setServletContext(new MockServletContext());
this.context.register(SecurityAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
assertNotNull(this.context.getBean(AuthenticationManager.class));
}
@Test
public void testOverrideAuthenticationManager() throws Exception {
this.context = new AnnotationConfigWebApplicationContext();
@@ -118,18 +124,6 @@ public class SecurityAutoConfigurationTests {
assertNotNull(this.context.getBean(JpaTransactionManager.class));
}
private static AnnotationConfigWebApplicationContext debugRefresh(
AnnotationConfigWebApplicationContext context) {
EnvironmentTestUtils.addEnvironment(context, "debug:true");
LoggingApplicationListener logging = new LoggingApplicationListener();
logging.onApplicationEvent(new ApplicationPreparedEvent(new SpringApplication(),
new String[0], context));
AutoConfigurationReportLoggingInitializer initializer = new AutoConfigurationReportLoggingInitializer();
initializer.initialize(context);
context.refresh();
return context;
}
@Configuration
@TestAutoConfigurationPackage(City.class)
protected static class EntityConfiguration {