Browse Source

Make a lazy AuthenticationManager if we think it's already configured

Instead of just blindly creating the default authentication manager, after
thic change we count the beans of type GlobalAuthenticationManagerConfigurer
and assume that if we detect more than we expect (one from Boot and one from
Spring Security) then the user is telling us they want to configure the
AuthenticationManager themselves.

Fixes gh-1801
pull/2035/head
Dave Syer 11 years ago
parent
commit
b20d02a31d
  1. 42
      spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/AuthenticationManagerConfiguration.java
  2. 112
      spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/SecurityAutoConfigurationTests.java

42
spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/AuthenticationManagerConfiguration.java

@ -32,7 +32,6 @@ import org.springframework.context.annotation.Bean; @@ -32,7 +32,6 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationEventPublisher;
import org.springframework.security.authentication.AuthenticationManager;
@ -42,6 +41,8 @@ import org.springframework.security.config.annotation.ObjectPostProcessor; @@ -42,6 +41,8 @@ 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.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.stereotype.Component;
/**
@ -57,8 +58,8 @@ import org.springframework.stereotype.Component; @@ -57,8 +58,8 @@ import org.springframework.stereotype.Component;
*/
@Configuration
@ConditionalOnBean(ObjectPostProcessor.class)
@ConditionalOnMissingBean(AuthenticationManager.class)
@Order(Ordered.LOWEST_PRECEDENCE - 3)
@ConditionalOnMissingBean({ AuthenticationManager.class })
@Order(0)
public class AuthenticationManagerConfiguration extends
GlobalAuthenticationConfigurerAdapter {
@ -84,18 +85,27 @@ public class AuthenticationManagerConfiguration extends @@ -84,18 +85,27 @@ public class AuthenticationManagerConfiguration extends
@Bean
@Primary
public AuthenticationManager authenticationManager(AuthenticationManagerBuilder auth)
throws Exception {
public AuthenticationManager authenticationManager(AuthenticationManagerBuilder auth,
ApplicationContext context) throws Exception {
if (isAuthenticationManagerAlreadyConfigured(context)) {
return new LazyAuthenticationManager(auth);
}
/*
* 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;
}
private boolean isAuthenticationManagerAlreadyConfigured(ApplicationContext context) {
return context.getBeanNamesForType(GlobalAuthenticationConfigurerAdapter.class).length > 2;
}
@Component
@ -142,8 +152,7 @@ public class AuthenticationManagerConfiguration extends @@ -142,8 +152,7 @@ public class AuthenticationManagerConfiguration extends
* methods are invoked before configure, which cannot be guaranteed at this point.</li>
* </ul>
*/
private class BootDefaultingAuthenticationConfigurerAdapter extends
GlobalAuthenticationConfigurerAdapter {
private class BootDefaultingAuthenticationConfigurerAdapter {
private AuthenticationManagerBuilder defaultAuth;
@ -159,7 +168,6 @@ public class AuthenticationManagerConfiguration extends @@ -159,7 +168,6 @@ public class AuthenticationManagerConfiguration extends
return this.defaultAuth;
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
if (auth.isConfigured()) {
this.defaultAuth = auth;
@ -188,4 +196,20 @@ public class AuthenticationManagerConfiguration extends @@ -188,4 +196,20 @@ public class AuthenticationManagerConfiguration extends
}
}
private static class LazyAuthenticationManager implements AuthenticationManager {
private AuthenticationManagerBuilder builder;
public LazyAuthenticationManager(AuthenticationManagerBuilder builder) {
this.builder = builder;
}
@Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
return builder.getOrBuild().authenticate(authentication);
}
}
}

112
spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/SecurityAutoConfigurationTests.java

@ -16,11 +16,17 @@ @@ -16,11 +16,17 @@
package org.springframework.boot.autoconfigure.security;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.After;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
@ -32,6 +38,7 @@ import org.springframework.context.ApplicationEvent; @@ -32,6 +38,7 @@ import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.mock.web.MockServletContext;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.security.authentication.AuthenticationManager;
@ -40,17 +47,16 @@ import org.springframework.security.authentication.TestingAuthenticationToken; @@ -40,17 +47,16 @@ import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.event.AuthenticationFailureBadCredentialsEvent;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configurers.GlobalAuthenticationConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* Tests for {@link SecurityAutoConfiguration}.
*
@ -138,7 +144,8 @@ public class SecurityAutoConfigurationTests { @@ -138,7 +144,8 @@ public class SecurityAutoConfigurationTests {
catch (BadCredentialsException e) {
// expected
}
assertTrue(wrapper.get() instanceof AuthenticationFailureBadCredentialsEvent);
assertTrue("Wrong event type: " + wrapper.get(),
wrapper.get() instanceof AuthenticationFailureBadCredentialsEvent);
}
@Test
@ -154,6 +161,55 @@ public class SecurityAutoConfigurationTests { @@ -154,6 +161,55 @@ public class SecurityAutoConfigurationTests {
this.context.getBean(AuthenticationManager.class));
}
@Test
public void testOverrideAuthenticationManagerAndInjectIntoSecurityFilter()
throws Exception {
this.context = new AnnotationConfigWebApplicationContext();
this.context.setServletContext(new MockServletContext());
this.context.register(TestAuthenticationConfiguration.class,
SecurityCustomizer.class, SecurityAutoConfiguration.class,
ServerPropertiesAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
assertEquals(
this.context.getBean(TestAuthenticationConfiguration.class).authenticationManager,
this.context.getBean(AuthenticationManager.class));
}
@Test
public void testOverrideAuthenticationManagerWithBuilderAndInjectIntoSecurityFilter()
throws Exception {
this.context = new AnnotationConfigWebApplicationContext();
this.context.setServletContext(new MockServletContext());
this.context.register(AuthenticationManagerCustomizer.class,
SecurityCustomizer.class, SecurityAutoConfiguration.class,
ServerPropertiesAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
UsernamePasswordAuthenticationToken user = new UsernamePasswordAuthenticationToken(
"foo", "bar",
AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));
assertNotNull(this.context.getBean(AuthenticationManager.class)
.authenticate(user));
}
@Test
public void testOverrideAuthenticationManagerWithBuilderAndInjectBuilderIntoSecurityFilter()
throws Exception {
this.context = new AnnotationConfigWebApplicationContext();
this.context.setServletContext(new MockServletContext());
this.context.register(AuthenticationManagerCustomizer.class,
WorkaroundSecurityCustomizer.class, SecurityAutoConfiguration.class,
ServerPropertiesAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
UsernamePasswordAuthenticationToken user = new UsernamePasswordAuthenticationToken(
"foo", "bar",
AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));
assertNotNull(this.context.getBean(AuthenticationManager.class)
.authenticate(user));
}
@Test
public void testJpaCoexistsHappily() throws Exception {
this.context = new AnnotationConfigWebApplicationContext();
@ -196,4 +252,48 @@ public class SecurityAutoConfigurationTests { @@ -196,4 +252,48 @@ public class SecurityAutoConfigurationTests {
}
@Configuration
protected static class SecurityCustomizer extends WebSecurityConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
}
@Configuration
protected static class WorkaroundSecurityCustomizer extends
WebSecurityConfigurerAdapter {
@Autowired
private AuthenticationManagerBuilder builder;
@SuppressWarnings("unused")
private AuthenticationManager authenticationManager;
@Override
protected void configure(HttpSecurity http) throws Exception {
this.authenticationManager = new AuthenticationManager() {
@Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
return WorkaroundSecurityCustomizer.this.builder.getOrBuild()
.authenticate(authentication);
}
};
}
}
@Configuration
@Order(-1)
protected static class AuthenticationManagerCustomizer extends
GlobalAuthenticationConfigurerAdapter {
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("foo").password("bar").roles("USER");
}
}
}

Loading…
Cancel
Save