Skip to content

Commit 760d6ec

Browse files
author
Rob Winch
committed
Fix Unnecessarily Adding Default Security User
Fixes gh-2567
1 parent 50e1f80 commit 760d6ec

File tree

2 files changed

+106
-13
lines changed

2 files changed

+106
-13
lines changed

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

Lines changed: 50 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
4444
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
4545
import org.springframework.security.config.annotation.authentication.configurers.GlobalAuthenticationConfigurerAdapter;
46+
import org.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer;
4647
import org.springframework.stereotype.Component;
4748

4849
/**
@@ -137,21 +138,58 @@ public BootDefaultingAuthenticationConfigurerAdapter(SecurityProperties security
137138

138139
@Override
139140
public void init(AuthenticationManagerBuilder auth) throws Exception {
140-
if (auth.isConfigured()) {
141-
return;
141+
auth.apply(new DefaultingInMemoryUserDetailsManagerConfigurer(this.security));
142+
}
143+
144+
/**
145+
* This is necessary to delay adding the default user.
146+
*
147+
* <ul>
148+
* <li>A GlobalAuthenticationConfigurerAdapter will initialize the
149+
* AuthenticationManagerBuilder with a Configurer which will be after any
150+
* GlobalAuthenticationConfigurerAdapter</li>
151+
* <li>BootDefaultingAuthenticationConfigurerAdapter will be invoked after all
152+
* GlobalAuthenticationConfigurerAdapter, but before the Configurers that were
153+
* added by other GlobalAuthenticationConfigurerAdapter instances</li>
154+
* <li>BootDefaultingAuthenticationConfigurerAdapter will add
155+
* DefaultingInMemoryUserDetailsManagerConfigurer after all Configurer instances</li>
156+
* <li>All init methods will be invoked</li>
157+
* <li>All configure methods will be invoked which is where the
158+
* AuthenticationProvider instances are setup</li>
159+
* <li>If no AuthenticationProviders were provided,
160+
* DefaultingInMemoryUserDetailsManagerConfigurer will default the value</li>
161+
* </ul>
162+
*
163+
* @author Rob Winch
164+
*/
165+
private static class DefaultingInMemoryUserDetailsManagerConfigurer extends
166+
InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> {
167+
private final SecurityProperties security;
168+
169+
public DefaultingInMemoryUserDetailsManagerConfigurer(
170+
SecurityProperties security) {
171+
this.security = security;
142172
}
143173

144-
User user = this.security.getUser();
145-
if (user.isDefaultPassword()) {
146-
logger.info("\n\nUsing default security password: " + user.getPassword()
147-
+ "\n\n");
174+
@Override
175+
public void configure(AuthenticationManagerBuilder auth) throws Exception {
176+
if (auth.isConfigured()) {
177+
return;
178+
}
179+
180+
User user = this.security.getUser();
181+
if (user.isDefaultPassword()) {
182+
logger.info("\n\nUsing default security password: "
183+
+ user.getPassword() + "\n");
184+
}
185+
186+
Set<String> roles = new LinkedHashSet<String>(user.getRole());
187+
withUser(user.getName()).password(user.getPassword()).roles(
188+
roles.toArray(new String[roles.size()]));
189+
190+
super.configure(auth);
148191
}
149192

150-
Set<String> roles = new LinkedHashSet<String>(user.getRole());
151-
auth.inMemoryAuthentication().withUser(user.getName())
152-
.password(user.getPassword())
153-
.roles(roles.toArray(new String[roles.size()]));
154193
}
155194
}
156-
157-
}
195+
}

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

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2014 the original author or authors.
2+
* Copyright 2012-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -40,6 +40,7 @@
4040
import org.springframework.security.authentication.BadCredentialsException;
4141
import org.springframework.security.authentication.TestingAuthenticationToken;
4242
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
43+
import org.springframework.security.authentication.event.AbstractAuthenticationEvent;
4344
import org.springframework.security.authentication.event.AuthenticationFailureBadCredentialsEvent;
4445
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
4546
import org.springframework.security.config.annotation.authentication.configurers.GlobalAuthenticationConfigurerAdapter;
@@ -226,6 +227,60 @@ public void testJpaCoexistsHappily() throws Exception {
226227
assertNotNull(this.context.getBean(JpaTransactionManager.class));
227228
}
228229

230+
@Test
231+
public void testDefaultUsernamePassword() throws Exception {
232+
this.context = new AnnotationConfigWebApplicationContext();
233+
this.context.setServletContext(new MockServletContext());
234+
235+
this.context.register(SecurityAutoConfiguration.class,
236+
ServerPropertiesAutoConfiguration.class);
237+
this.context.refresh();
238+
239+
SecurityProperties security = this.context.getBean(SecurityProperties.class);
240+
AuthenticationManager manager = this.context.getBean(AuthenticationManager.class);
241+
242+
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
243+
security.getUser().getName(), security.getUser().getPassword());
244+
assertNotNull(manager.authenticate(token));
245+
}
246+
247+
@Test
248+
public void testCustomAuthenticationDoesNotAuthenticateWithBootSecurityUser()
249+
throws Exception {
250+
this.context = new AnnotationConfigWebApplicationContext();
251+
this.context.setServletContext(new MockServletContext());
252+
253+
this.context.register(AuthenticationManagerCustomizer.class,
254+
SecurityAutoConfiguration.class, ServerPropertiesAutoConfiguration.class);
255+
this.context.refresh();
256+
257+
SecurityProperties security = this.context.getBean(SecurityProperties.class);
258+
AuthenticationManager manager = this.context.getBean(AuthenticationManager.class);
259+
260+
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
261+
security.getUser().getName(), security.getUser().getPassword());
262+
try {
263+
manager.authenticate(token);
264+
fail("Expected Exception");
265+
}
266+
catch (AuthenticationException success) {
267+
}
268+
269+
token = new UsernamePasswordAuthenticationToken("foo", "bar");
270+
assertNotNull(manager.authenticate(token));
271+
}
272+
273+
private static final class AuthenticationListener implements
274+
ApplicationListener<AbstractAuthenticationEvent> {
275+
276+
private ApplicationEvent event;
277+
278+
@Override
279+
public void onApplicationEvent(AbstractAuthenticationEvent event) {
280+
this.event = event;
281+
}
282+
}
283+
229284
@Configuration
230285
@TestAutoConfigurationPackage(City.class)
231286
protected static class EntityConfiguration {

0 commit comments

Comments
 (0)