From b20d02a31db3670c00bc52f663f63640760df3b0 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Sat, 1 Nov 2014 17:41:52 +0000 Subject: [PATCH 1/2] 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 --- .../AuthenticationManagerConfiguration.java | 42 +++++-- .../SecurityAutoConfigurationTests.java | 112 +++++++++++++++++- 2 files changed, 139 insertions(+), 15 deletions(-) diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/AuthenticationManagerConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/AuthenticationManagerConfiguration.java index 16a2e4e0ea7..ee609d11b1f 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/AuthenticationManagerConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/AuthenticationManagerConfiguration.java @@ -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; 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; */ @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 @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 * methods are invoked before configure, which cannot be guaranteed at this point. * */ - private class BootDefaultingAuthenticationConfigurerAdapter extends - GlobalAuthenticationConfigurerAdapter { + private class BootDefaultingAuthenticationConfigurerAdapter { private AuthenticationManagerBuilder defaultAuth; @@ -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 } } + 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); + } + + } + } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/SecurityAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/SecurityAutoConfigurationTests.java index 112a6e03f42..179aeb6703a 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/SecurityAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/SecurityAutoConfigurationTests.java @@ -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; 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; 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 { 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 { 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 { } + @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"); + } + + } + } From 8ba71c88f544ab04071678b6bca0804cd9b075f5 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Sat, 1 Nov 2014 19:46:04 -0700 Subject: [PATCH 2/2] Polish --- .../security/AuthenticationManagerConfiguration.java | 2 +- .../security/SecurityAutoConfigurationTests.java | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/AuthenticationManagerConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/AuthenticationManagerConfiguration.java index ee609d11b1f..08b01c2261b 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/AuthenticationManagerConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/AuthenticationManagerConfiguration.java @@ -207,7 +207,7 @@ public class AuthenticationManagerConfiguration extends @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { - return builder.getOrBuild().authenticate(authentication); + return this.builder.getOrBuild().authenticate(authentication); } } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/SecurityAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/SecurityAutoConfigurationTests.java index 179aeb6703a..ed2b18a7117 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/SecurityAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/SecurityAutoConfigurationTests.java @@ -16,11 +16,6 @@ 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; @@ -57,6 +52,11 @@ 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}. *