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,16 +292,21 @@ Try it out:
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
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
`@Bean` definition for an `AuthenticationManager`, e.g. in your
`SampleController`:
point to a database or directory server, you can add the `@EnableGlobalAuthentication`
annotation and configure the global `AuthenticationManagerBuilder` as shown below:
@Controller
@EnableAutoConfiguration
@EnableGlobalAuthentication
public class SampleController {
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("client").password("secret").roles("USER");
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("client").password("secret").roles("USER");
...
}
Try it out:

@ -98,7 +98,9 @@ public class ManagementSecurityAutoConfigurationTests {
private UserDetails getUser() {
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);
UserDetailsService service = (UserDetailsService) ReflectionTestUtils.getField(
provider, "userDetailsService");

@ -16,20 +16,26 @@
package org.springframework.boot.autoconfigure.security;
import java.util.LinkedHashSet;
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.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.security.SecurityProperties.User;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
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.SecurityConfigurer;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.authentication.configurers.GlobalAuthenticationConfigurerAdapter;
/**
* 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)
@ConditionalOnWebApplication
@Order(Ordered.LOWEST_PRECEDENCE - 3)
public class AuthenticationManagerConfiguration implements
WebSecurityConfigurer<WebSecurity> {
public class AuthenticationManagerConfiguration extends GlobalAuthenticationConfigurerAdapter {
@Autowired
private SecurityProperties security;
private static Log logger = LogFactory.getLog(AuthenticationManagerConfiguration.class);
@Autowired
private List<SecurityPrequisite> dependencies;
@Override
public void init(WebSecurity builder) throws Exception {
}
@Override
public void configure(WebSecurity builder) throws Exception {
}
@Autowired
private ObjectPostProcessor<Object> objectPostProcessor;
@Autowired
public void authentication(AuthenticationManagerBuilder builder) throws Exception {
SecurityAutoConfiguration.authentication(builder, this.security);
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
public void configure(AuthenticationManagerBuilder auth)
throws Exception {
if(auth.isConfigured()) {
return;
}
User user = AuthenticationManagerConfiguration.this.security.getUser();
if (user.isDefaultPassword()) {
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;
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.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
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.util.ReflectionUtils;
/**
* {@link EnableAutoConfiguration Auto-configuration} for Spring Security. Provides an
@ -58,68 +45,9 @@ import org.springframework.util.ReflectionUtils;
@Import({ SpringBootWebSecurityConfiguration.class,
AuthenticationManagerConfiguration.class })
public class SecurityAutoConfiguration {
private static Log logger = LogFactory.getLog(SecurityAutoConfiguration.class);
@Bean
@ConditionalOnMissingBean
public SecurityProperties 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