Skip to content

Commit b20d02a

Browse files
author
Dave Syer
committed
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
1 parent 0f17142 commit b20d02a

File tree

2 files changed

+139
-15
lines changed

2 files changed

+139
-15
lines changed

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

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
import org.springframework.context.annotation.Configuration;
3333
import org.springframework.context.annotation.Primary;
3434
import org.springframework.context.event.ContextRefreshedEvent;
35-
import org.springframework.core.Ordered;
3635
import org.springframework.core.annotation.Order;
3736
import org.springframework.security.authentication.AuthenticationEventPublisher;
3837
import org.springframework.security.authentication.AuthenticationManager;
@@ -42,6 +41,8 @@
4241
import org.springframework.security.config.annotation.SecurityConfigurer;
4342
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
4443
import org.springframework.security.config.annotation.authentication.configurers.GlobalAuthenticationConfigurerAdapter;
44+
import org.springframework.security.core.Authentication;
45+
import org.springframework.security.core.AuthenticationException;
4546
import org.springframework.stereotype.Component;
4647

4748
/**
@@ -57,8 +58,8 @@
5758
*/
5859
@Configuration
5960
@ConditionalOnBean(ObjectPostProcessor.class)
60-
@ConditionalOnMissingBean(AuthenticationManager.class)
61-
@Order(Ordered.LOWEST_PRECEDENCE - 3)
61+
@ConditionalOnMissingBean({ AuthenticationManager.class })
62+
@Order(0)
6263
public class AuthenticationManagerConfiguration extends
6364
GlobalAuthenticationConfigurerAdapter {
6465

@@ -84,18 +85,27 @@ public class AuthenticationManagerConfiguration extends
8485

8586
@Bean
8687
@Primary
87-
public AuthenticationManager authenticationManager(AuthenticationManagerBuilder auth)
88-
throws Exception {
88+
public AuthenticationManager authenticationManager(AuthenticationManagerBuilder auth,
89+
ApplicationContext context) throws Exception {
90+
91+
if (isAuthenticationManagerAlreadyConfigured(context)) {
92+
return new LazyAuthenticationManager(auth);
93+
}
94+
8995
/*
9096
* This AuthenticationManagerBuilder is for the global AuthenticationManager
9197
*/
9298
BootDefaultingAuthenticationConfigurerAdapter configurer = new BootDefaultingAuthenticationConfigurerAdapter();
93-
configurer.init(auth);
9499
configurer.configure(auth);
95100
AuthenticationManager manager = configurer.getAuthenticationManagerBuilder()
96101
.getOrBuild();
97102
configurer.configureParent(auth);
98103
return manager;
104+
105+
}
106+
107+
private boolean isAuthenticationManagerAlreadyConfigured(ApplicationContext context) {
108+
return context.getBeanNamesForType(GlobalAuthenticationConfigurerAdapter.class).length > 2;
99109
}
100110

101111
@Component
@@ -142,8 +152,7 @@ public void onApplicationEvent(ContextRefreshedEvent event) {
142152
* methods are invoked before configure, which cannot be guaranteed at this point.</li>
143153
* </ul>
144154
*/
145-
private class BootDefaultingAuthenticationConfigurerAdapter extends
146-
GlobalAuthenticationConfigurerAdapter {
155+
private class BootDefaultingAuthenticationConfigurerAdapter {
147156

148157
private AuthenticationManagerBuilder defaultAuth;
149158

@@ -159,7 +168,6 @@ public AuthenticationManagerBuilder getAuthenticationManagerBuilder() {
159168
return this.defaultAuth;
160169
}
161170

162-
@Override
163171
public void configure(AuthenticationManagerBuilder auth) throws Exception {
164172
if (auth.isConfigured()) {
165173
this.defaultAuth = auth;
@@ -188,4 +196,20 @@ public void configure(AuthenticationManagerBuilder auth) throws Exception {
188196
}
189197
}
190198

199+
private static class LazyAuthenticationManager implements AuthenticationManager {
200+
201+
private AuthenticationManagerBuilder builder;
202+
203+
public LazyAuthenticationManager(AuthenticationManagerBuilder builder) {
204+
this.builder = builder;
205+
}
206+
207+
@Override
208+
public Authentication authenticate(Authentication authentication)
209+
throws AuthenticationException {
210+
return builder.getOrBuild().authenticate(authentication);
211+
}
212+
213+
}
214+
191215
}

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

Lines changed: 106 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,17 @@
1616

1717
package org.springframework.boot.autoconfigure.security;
1818

19+
import static org.junit.Assert.assertEquals;
20+
import static org.junit.Assert.assertNotNull;
21+
import static org.junit.Assert.assertTrue;
22+
import static org.junit.Assert.fail;
23+
1924
import java.util.List;
2025
import java.util.concurrent.atomic.AtomicReference;
2126

2227
import org.junit.After;
2328
import org.junit.Test;
29+
import org.springframework.beans.factory.annotation.Autowired;
2430
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
2531
import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage;
2632
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
@@ -32,6 +38,7 @@
3238
import org.springframework.context.ApplicationListener;
3339
import org.springframework.context.annotation.Bean;
3440
import org.springframework.context.annotation.Configuration;
41+
import org.springframework.core.annotation.Order;
3542
import org.springframework.mock.web.MockServletContext;
3643
import org.springframework.orm.jpa.JpaTransactionManager;
3744
import org.springframework.security.authentication.AuthenticationManager;
@@ -40,17 +47,16 @@
4047
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
4148
import org.springframework.security.authentication.event.AuthenticationFailureBadCredentialsEvent;
4249
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
50+
import org.springframework.security.config.annotation.authentication.configurers.GlobalAuthenticationConfigurerAdapter;
51+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
52+
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
4353
import org.springframework.security.core.Authentication;
4454
import org.springframework.security.core.AuthenticationException;
55+
import org.springframework.security.core.authority.AuthorityUtils;
4556
import org.springframework.security.web.FilterChainProxy;
4657
import org.springframework.security.web.SecurityFilterChain;
4758
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
4859

49-
import static org.junit.Assert.assertEquals;
50-
import static org.junit.Assert.assertNotNull;
51-
import static org.junit.Assert.assertTrue;
52-
import static org.junit.Assert.fail;
53-
5460
/**
5561
* Tests for {@link SecurityAutoConfiguration}.
5662
*
@@ -138,7 +144,8 @@ public void onApplicationEvent(ApplicationEvent event) {
138144
catch (BadCredentialsException e) {
139145
// expected
140146
}
141-
assertTrue(wrapper.get() instanceof AuthenticationFailureBadCredentialsEvent);
147+
assertTrue("Wrong event type: " + wrapper.get(),
148+
wrapper.get() instanceof AuthenticationFailureBadCredentialsEvent);
142149
}
143150

144151
@Test
@@ -154,6 +161,55 @@ public void testOverrideAuthenticationManager() throws Exception {
154161
this.context.getBean(AuthenticationManager.class));
155162
}
156163

164+
@Test
165+
public void testOverrideAuthenticationManagerAndInjectIntoSecurityFilter()
166+
throws Exception {
167+
this.context = new AnnotationConfigWebApplicationContext();
168+
this.context.setServletContext(new MockServletContext());
169+
this.context.register(TestAuthenticationConfiguration.class,
170+
SecurityCustomizer.class, SecurityAutoConfiguration.class,
171+
ServerPropertiesAutoConfiguration.class,
172+
PropertyPlaceholderAutoConfiguration.class);
173+
this.context.refresh();
174+
assertEquals(
175+
this.context.getBean(TestAuthenticationConfiguration.class).authenticationManager,
176+
this.context.getBean(AuthenticationManager.class));
177+
}
178+
179+
@Test
180+
public void testOverrideAuthenticationManagerWithBuilderAndInjectIntoSecurityFilter()
181+
throws Exception {
182+
this.context = new AnnotationConfigWebApplicationContext();
183+
this.context.setServletContext(new MockServletContext());
184+
this.context.register(AuthenticationManagerCustomizer.class,
185+
SecurityCustomizer.class, SecurityAutoConfiguration.class,
186+
ServerPropertiesAutoConfiguration.class,
187+
PropertyPlaceholderAutoConfiguration.class);
188+
this.context.refresh();
189+
UsernamePasswordAuthenticationToken user = new UsernamePasswordAuthenticationToken(
190+
"foo", "bar",
191+
AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));
192+
assertNotNull(this.context.getBean(AuthenticationManager.class)
193+
.authenticate(user));
194+
}
195+
196+
@Test
197+
public void testOverrideAuthenticationManagerWithBuilderAndInjectBuilderIntoSecurityFilter()
198+
throws Exception {
199+
this.context = new AnnotationConfigWebApplicationContext();
200+
this.context.setServletContext(new MockServletContext());
201+
this.context.register(AuthenticationManagerCustomizer.class,
202+
WorkaroundSecurityCustomizer.class, SecurityAutoConfiguration.class,
203+
ServerPropertiesAutoConfiguration.class,
204+
PropertyPlaceholderAutoConfiguration.class);
205+
this.context.refresh();
206+
UsernamePasswordAuthenticationToken user = new UsernamePasswordAuthenticationToken(
207+
"foo", "bar",
208+
AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));
209+
assertNotNull(this.context.getBean(AuthenticationManager.class)
210+
.authenticate(user));
211+
}
212+
157213
@Test
158214
public void testJpaCoexistsHappily() throws Exception {
159215
this.context = new AnnotationConfigWebApplicationContext();
@@ -196,4 +252,48 @@ public Authentication authenticate(Authentication authentication)
196252

197253
}
198254

255+
@Configuration
256+
protected static class SecurityCustomizer extends WebSecurityConfigurerAdapter {
257+
258+
@Autowired
259+
private AuthenticationManager authenticationManager;
260+
261+
}
262+
263+
@Configuration
264+
protected static class WorkaroundSecurityCustomizer extends
265+
WebSecurityConfigurerAdapter {
266+
267+
@Autowired
268+
private AuthenticationManagerBuilder builder;
269+
270+
@SuppressWarnings("unused")
271+
private AuthenticationManager authenticationManager;
272+
273+
@Override
274+
protected void configure(HttpSecurity http) throws Exception {
275+
this.authenticationManager = new AuthenticationManager() {
276+
@Override
277+
public Authentication authenticate(Authentication authentication)
278+
throws AuthenticationException {
279+
return WorkaroundSecurityCustomizer.this.builder.getOrBuild()
280+
.authenticate(authentication);
281+
}
282+
};
283+
}
284+
285+
}
286+
287+
@Configuration
288+
@Order(-1)
289+
protected static class AuthenticationManagerCustomizer extends
290+
GlobalAuthenticationConfigurerAdapter {
291+
292+
@Override
293+
public void init(AuthenticationManagerBuilder auth) throws Exception {
294+
auth.inMemoryAuthentication().withUser("foo").password("bar").roles("USER");
295+
}
296+
297+
}
298+
199299
}

0 commit comments

Comments
 (0)