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
This commit is contained in:
parent
0f17142366
commit
b20d02a31d
|
@ -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.</li>
|
||||
* </ul>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue