Update to Spring Security 3.2.1

Also change strategy for defaulting of Authentication. Spring
Boot authentication defaults are now encapsulated and can easily
be overridden by a user defined AuthenticationManager.
pull/415/head
Rob Winch 11 years ago committed by Dave Syer
parent 13e040c06e
commit 6b0eba3759
Notes: Phillip Webb 11 years ago
Fixes gh-338

@ -292,11 +292,13 @@ Try it out:
The default auto configuration has an in-memory user database with one The default auto configuration has an in-memory user database with one
entry, and the `<password>` value has to be read from the logs (at entry, and the `<password>` value has to be read from the logs (at
INFO level) by default. If you want to extend or expand that, or INFO level) by default. If you want to extend or expand that, or
point to a database or directory server, you only need to provide a point to a database or directory server, you can add the `@EnableGlobalAuthentication`
`@Bean` definition for an `AuthenticationManager`, e.g. in your annotation and configure the global `AuthenticationManagerBuilder` as shown below:
`SampleController`:
@Controller
@EnableAutoConfiguration
@EnableGlobalAuthentication
public class SampleController {
@Autowired @Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
@ -304,6 +306,9 @@ point to a database or directory server, you only need to provide a
.withUser("client").password("secret").roles("USER"); .withUser("client").password("secret").roles("USER");
} }
...
}
Try it out: Try it out:
$ curl user:password@localhost:8080/ $ curl user:password@localhost:8080/

@ -98,7 +98,9 @@ public class ManagementSecurityAutoConfigurationTests {
private UserDetails getUser() { private UserDetails getUser() {
ProviderManager manager = this.context.getBean(ProviderManager.class); ProviderManager manager = this.context.getBean(ProviderManager.class);
DaoAuthenticationProvider provider = (DaoAuthenticationProvider) manager ProviderManager parent = (ProviderManager) ReflectionTestUtils.getField(
manager, "parent");
DaoAuthenticationProvider provider = (DaoAuthenticationProvider) parent
.getProviders().get(0); .getProviders().get(0);
UserDetailsService service = (UserDetailsService) ReflectionTestUtils.getField( UserDetailsService service = (UserDetailsService) ReflectionTestUtils.getField(
provider, "userDetailsService"); provider, "userDetailsService");

@ -16,20 +16,26 @@
package org.springframework.boot.autoconfigure.security; package org.springframework.boot.autoconfigure.security;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.security.SecurityProperties.User;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.ObjectPostProcessor; 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.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.WebSecurityConfigurer; import org.springframework.security.config.annotation.authentication.configurers.GlobalAuthenticationConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
/** /**
* Configuration for a Spring Security in-memory {@link AuthenticationManager}. * Configuration for a Spring Security in-memory {@link AuthenticationManager}.
@ -41,26 +47,77 @@ import org.springframework.security.config.annotation.web.builders.WebSecurity;
@ConditionalOnMissingBean(AuthenticationManager.class) @ConditionalOnMissingBean(AuthenticationManager.class)
@ConditionalOnWebApplication @ConditionalOnWebApplication
@Order(Ordered.LOWEST_PRECEDENCE - 3) @Order(Ordered.LOWEST_PRECEDENCE - 3)
public class AuthenticationManagerConfiguration implements public class AuthenticationManagerConfiguration extends GlobalAuthenticationConfigurerAdapter {
WebSecurityConfigurer<WebSecurity> {
@Autowired private static Log logger = LogFactory.getLog(AuthenticationManagerConfiguration.class);
private SecurityProperties security;
@Autowired @Autowired
private List<SecurityPrequisite> dependencies; private List<SecurityPrequisite> dependencies;
@Override @Autowired
public void init(WebSecurity builder) throws Exception { private ObjectPostProcessor<Object> objectPostProcessor;
@Autowired
private SecurityProperties security;
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.apply(new BootDefaultingAuthenticationConfigurerAdapter());
} }
/**
* We must add {@link BootDefaultingAuthenticationConfigurerAdapter} in the
* init phase of the last {@link GlobalAuthenticationConfigurerAdapter}. The
* reason is that the typical flow is something like:
*
* <ul>
* <li>A
* {@link GlobalAuthenticationConfigurerAdapter#init(AuthenticationManagerBuilder)}
* exists that adds a {@link SecurityConfigurer} to the
* {@link AuthenticationManagerBuilder}</li>
* <li>
* {@link AuthenticationManagerConfiguration#init(AuthenticationManagerBuilder)}
* adds BootDefaultingAuthenticationConfigurerAdapter so it is after the
* {@link SecurityConfigurer} in the first step</li>
* <li>We then can default an {@link AuthenticationProvider} if necessary.
* Note we can only invoke the
* {@link AuthenticationManagerBuilder#authenticationProvider(AuthenticationProvider)}
* method since all other methods add a {@link SecurityConfigurer} which is
* not allowed in the configure stage. It is not allowed because we
* guarantee all init methods are invoked before configure, which cannot be
* guaranteed at this point.</li>
* </ul>
*
* @author Rob Winch
*/
private class BootDefaultingAuthenticationConfigurerAdapter extends GlobalAuthenticationConfigurerAdapter {
@Override @Override
public void configure(WebSecurity builder) throws Exception { public void configure(AuthenticationManagerBuilder auth)
throws Exception {
if(auth.isConfigured()) {
return;
} }
@Autowired User user = AuthenticationManagerConfiguration.this.security.getUser();
public void authentication(AuthenticationManagerBuilder builder) throws Exception { if (user.isDefaultPassword()) {
SecurityAutoConfiguration.authentication(builder, this.security); logger.info("\n\nUsing default password for application endpoints: "
+ user.getPassword() + "\n\n");
} }
AuthenticationManagerBuilder defaultAuth = new AuthenticationManagerBuilder(objectPostProcessor);
Set<String> roles = new LinkedHashSet<String>(user.getRole());
AuthenticationManager parent = defaultAuth.
inMemoryAuthentication()
.withUser(user.getName())
.password(user.getPassword())
.roles(roles.toArray(new String[roles.size()]))
.and()
.and()
.build();
auth.parentAuthenticationManager(parent);
}
}
} }

@ -16,28 +16,15 @@
package org.springframework.boot.autoconfigure.security; package org.springframework.boot.autoconfigure.security;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.security.SecurityProperties.User;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.util.ReflectionUtils;
/** /**
* {@link EnableAutoConfiguration Auto-configuration} for Spring Security. Provides an * {@link EnableAutoConfiguration Auto-configuration} for Spring Security. Provides an
@ -58,68 +45,9 @@ import org.springframework.util.ReflectionUtils;
@Import({ SpringBootWebSecurityConfiguration.class, @Import({ SpringBootWebSecurityConfiguration.class,
AuthenticationManagerConfiguration.class }) AuthenticationManagerConfiguration.class })
public class SecurityAutoConfiguration { public class SecurityAutoConfiguration {
private static Log logger = LogFactory.getLog(SecurityAutoConfiguration.class);
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public SecurityProperties securityProperties() { public SecurityProperties securityProperties() {
return new SecurityProperties(); return new SecurityProperties();
} }
@Bean
@ConditionalOnBean(AuthenticationManagerBuilder.class)
@ConditionalOnMissingBean
public AuthenticationManager authenticationManager(
AuthenticationManagerBuilder builder, ObjectPostProcessor<Object> processor)
throws Exception {
if (!isBuilt(builder)) {
authentication(builder, securityProperties());
}
else if (builder.getOrBuild() == null) {
builder = new AuthenticationManagerBuilder(processor);
authentication(builder, securityProperties());
}
return builder.getOrBuild();
}
/**
* Convenience method for building the default AuthenticationManager from
* SecurityProperties.
*
* @param builder the AuthenticationManagerBuilder to use
* @param security the SecurityProperties in use
*/
public static void authentication(AuthenticationManagerBuilder builder,
SecurityProperties security) throws Exception {
if (isBuilt(builder)) {
return;
}
User user = security.getUser();
if (user.isDefaultPassword()) {
logger.info("\n\nUsing default password for application endpoints: "
+ user.getPassword() + "\n\n");
}
Set<String> roles = new LinkedHashSet<String>(user.getRole());
builder.inMemoryAuthentication().withUser(user.getName())
.password(user.getPassword())
.roles(roles.toArray(new String[roles.size()]));
}
private static boolean isBuilt(AuthenticationManagerBuilder builder) {
Method configurers = ReflectionUtils.findMethod(
AbstractConfiguredSecurityBuilder.class, "getConfigurers");
Method unbuilt = ReflectionUtils.findMethod(
AbstractConfiguredSecurityBuilder.class, "isUnbuilt");
ReflectionUtils.makeAccessible(configurers);
ReflectionUtils.makeAccessible(unbuilt);
return !((Collection<?>) ReflectionUtils.invokeMethod(configurers, builder))
.isEmpty() || !((Boolean) ReflectionUtils.invokeMethod(unbuilt, builder));
}
} }
Loading…
Cancel
Save