SEC-629: authentication-provider doesn't support caching.
http://jira.springframework.org/browse/SEC-629. Added support for cache-ref elements on jdbc-user-service and ldap-user-service
This commit is contained in:
parent
db6fafaf56
commit
1463b9769d
|
@ -1,8 +1,12 @@
|
||||||
package org.springframework.security.config;
|
package org.springframework.security.config;
|
||||||
|
|
||||||
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
|
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||||
import org.springframework.beans.factory.xml.ParserContext;
|
import org.springframework.beans.factory.xml.ParserContext;
|
||||||
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
|
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||||
|
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||||
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
@ -12,16 +16,53 @@ import org.w3c.dom.Element;
|
||||||
* @author Luke Taylor
|
* @author Luke Taylor
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
*/
|
*/
|
||||||
public class AbstractUserDetailsServiceBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
|
public abstract class AbstractUserDetailsServiceBeanDefinitionParser implements BeanDefinitionParser {
|
||||||
|
private static final String CACHE_REF = "cache-ref";
|
||||||
|
public static final String CACHING_SUFFIX = ".caching";
|
||||||
|
|
||||||
|
/** UserDetailsService bean Id. For use in a stateful context (i.e. in AuthenticationProviderBDP) */
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
protected abstract Class getBeanClass(Element element);
|
||||||
|
|
||||||
|
protected abstract void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder);
|
||||||
|
|
||||||
|
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||||
|
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(getBeanClass(element));
|
||||||
|
|
||||||
|
doParse(element, parserContext, builder);
|
||||||
|
|
||||||
|
RootBeanDefinition userService = (RootBeanDefinition) builder.getBeanDefinition();
|
||||||
|
String beanId = resolveId(element, userService, parserContext);
|
||||||
|
|
||||||
|
parserContext.getRegistry().registerBeanDefinition(beanId, userService);
|
||||||
|
|
||||||
|
String cacheRef = element.getAttribute(CACHE_REF);
|
||||||
|
|
||||||
|
// Register a caching version of the user service if there's a cache-ref
|
||||||
|
if (StringUtils.hasText(cacheRef)) {
|
||||||
|
BeanDefinitionBuilder cachingUSBuilder = BeanDefinitionBuilder.rootBeanDefinition(CachingUserDetailsService.class);
|
||||||
|
cachingUSBuilder.addConstructorArgReference(beanId);
|
||||||
|
|
||||||
|
cachingUSBuilder.addPropertyValue("userCache", new RuntimeBeanReference(cacheRef));
|
||||||
|
BeanDefinition cachingUserService = cachingUSBuilder.getBeanDefinition();
|
||||||
|
parserContext.getRegistry().registerBeanDefinition(beanId + CACHING_SUFFIX, cachingUserService);
|
||||||
|
}
|
||||||
|
|
||||||
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) throws BeanDefinitionStoreException {
|
id = beanId;
|
||||||
String id = super.resolveId(element, definition, parserContext);
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext)
|
||||||
|
throws BeanDefinitionStoreException {
|
||||||
|
|
||||||
|
String id = element.getAttribute("id");
|
||||||
|
|
||||||
if (StringUtils.hasText(id)) {
|
if (StringUtils.hasText(id)) {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it's nested in a parent auth-provider, generate an id automatically
|
|
||||||
if(Elements.AUTHENTICATION_PROVIDER.equals(element.getParentNode().getNodeName())) {
|
if(Elements.AUTHENTICATION_PROVIDER.equals(element.getParentNode().getNodeName())) {
|
||||||
return parserContext.getReaderContext().generateBeanName(definition);
|
return parserContext.getReaderContext().generateBeanName(definition);
|
||||||
}
|
}
|
||||||
|
@ -34,4 +75,8 @@ public class AbstractUserDetailsServiceBeanDefinitionParser extends AbstractSing
|
||||||
|
|
||||||
return BeanIds.USER_DETAILS_SERVICE;
|
return BeanIds.USER_DETAILS_SERVICE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,19 @@
|
||||||
package org.springframework.security.config;
|
package org.springframework.security.config;
|
||||||
|
|
||||||
import org.springframework.security.providers.dao.DaoAuthenticationProvider;
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.beans.PropertyValue;
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
|
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||||
|
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||||
|
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||||
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||||
import org.springframework.beans.factory.xml.ParserContext;
|
import org.springframework.beans.factory.xml.ParserContext;
|
||||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
import org.springframework.core.Ordered;
|
||||||
import org.springframework.util.xml.DomUtils;
|
import org.springframework.security.providers.dao.DaoAuthenticationProvider;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.util.xml.DomUtils;
|
||||||
import org.w3c.dom.Element;
|
import org.w3c.dom.Element;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -36,40 +41,88 @@ class AuthenticationProviderBeanDefinitionParser implements BeanDefinitionParser
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigUtils.getRegisteredProviders(parserContext).add(authProvider);
|
|
||||||
|
|
||||||
String ref = element.getAttribute(ATT_USER_DETAILS_REF);
|
|
||||||
Element userServiceElt = DomUtils.getChildElementByTagName(element, Elements.USER_SERVICE);
|
Element userServiceElt = DomUtils.getChildElementByTagName(element, Elements.USER_SERVICE);
|
||||||
Element jdbcUserServiceElt = DomUtils.getChildElementByTagName(element, Elements.JDBC_USER_SERVICE);
|
Element jdbcUserServiceElt = DomUtils.getChildElementByTagName(element, Elements.JDBC_USER_SERVICE);
|
||||||
Element ldapUserServiceElt = DomUtils.getChildElementByTagName(element, Elements.LDAP_USER_SERVICE);
|
Element ldapUserServiceElt = DomUtils.getChildElementByTagName(element, Elements.LDAP_USER_SERVICE);
|
||||||
|
|
||||||
if (StringUtils.hasText(ref)) {
|
// We need to register the provider to access it in the post processor to check if it has a cache
|
||||||
|
final String id = parserContext.getReaderContext().generateBeanName(authProvider);
|
||||||
|
parserContext.getRegistry().registerBeanDefinition(id, authProvider);
|
||||||
|
|
||||||
|
String ref = element.getAttribute(ATT_USER_DETAILS_REF);
|
||||||
|
|
||||||
|
if (StringUtils.hasText(ref)) {
|
||||||
if (userServiceElt != null || jdbcUserServiceElt != null || ldapUserServiceElt != null) {
|
if (userServiceElt != null || jdbcUserServiceElt != null || ldapUserServiceElt != null) {
|
||||||
parserContext.getReaderContext().error("The ref attribute cannot be used in combination with child" +
|
parserContext.getReaderContext().error("The " + ATT_USER_DETAILS_REF + " attribute cannot be used in combination with child" +
|
||||||
"elements '" + Elements.USER_SERVICE + "', '" + Elements.JDBC_USER_SERVICE + "' or '" +
|
"elements '" + Elements.USER_SERVICE + "', '" + Elements.JDBC_USER_SERVICE + "' or '" +
|
||||||
Elements.LDAP_USER_SERVICE + "'", element);
|
Elements.LDAP_USER_SERVICE + "'", element);
|
||||||
}
|
}
|
||||||
|
|
||||||
authProvider.getPropertyValues().addPropertyValue("userDetailsService", new RuntimeBeanReference(ref));
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use the child elements to create the UserDetailsService
|
|
||||||
BeanDefinition userDetailsService = null;
|
|
||||||
|
|
||||||
if (userServiceElt != null) {
|
|
||||||
userDetailsService = new UserServiceBeanDefinitionParser().parse(userServiceElt, parserContext);
|
|
||||||
} else if (jdbcUserServiceElt != null) {
|
|
||||||
userDetailsService = new JdbcUserServiceBeanDefinitionParser().parse(jdbcUserServiceElt, parserContext);
|
|
||||||
} else if (ldapUserServiceElt != null) {
|
|
||||||
userDetailsService = new LdapUserServiceBeanDefinitionParser().parse(ldapUserServiceElt, parserContext);
|
|
||||||
} else {
|
} else {
|
||||||
parserContext.getReaderContext().error("A user-service is required", element);
|
// Use the child elements to create the UserDetailsService
|
||||||
|
AbstractUserDetailsServiceBeanDefinitionParser parser = null;
|
||||||
|
Element elt = null;
|
||||||
|
|
||||||
|
if (userServiceElt != null) {
|
||||||
|
elt = userServiceElt;
|
||||||
|
parser = new UserServiceBeanDefinitionParser();
|
||||||
|
} else if (jdbcUserServiceElt != null) {
|
||||||
|
elt = jdbcUserServiceElt;
|
||||||
|
parser = new JdbcUserServiceBeanDefinitionParser();
|
||||||
|
} else if (ldapUserServiceElt != null) {
|
||||||
|
elt = ldapUserServiceElt;
|
||||||
|
parser = new LdapUserServiceBeanDefinitionParser();
|
||||||
|
} else {
|
||||||
|
parserContext.getReaderContext().error("A user-service is required", element);
|
||||||
|
}
|
||||||
|
|
||||||
|
parser.parse(elt, parserContext);
|
||||||
|
ref = parser.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
authProvider.getPropertyValues().addPropertyValue("userDetailsService", new RuntimeBeanReference(ref));
|
||||||
|
|
||||||
authProvider.getPropertyValues().addPropertyValue("userDetailsService", userDetailsService);
|
BeanDefinitionBuilder cacheResolverBldr = BeanDefinitionBuilder.rootBeanDefinition(AuthenticationProviderCacheResolver.class);
|
||||||
|
cacheResolverBldr.addConstructorArg(id);
|
||||||
|
cacheResolverBldr.addConstructorArg(ref);
|
||||||
|
cacheResolverBldr.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||||
|
BeanDefinition cacheResolver = cacheResolverBldr.getBeanDefinition();
|
||||||
|
parserContext.getRegistry().registerBeanDefinition(
|
||||||
|
parserContext.getReaderContext().generateBeanName(cacheResolver), cacheResolver);
|
||||||
|
|
||||||
|
ConfigUtils.getRegisteredProviders(parserContext).add(new RuntimeBeanReference(id));
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the registered user service bean has an associated cache and, if so, sets it on the
|
||||||
|
* authentication provider.
|
||||||
|
*/
|
||||||
|
static class AuthenticationProviderCacheResolver implements BeanFactoryPostProcessor, Ordered {
|
||||||
|
private String providerId;
|
||||||
|
private String userServiceId;
|
||||||
|
|
||||||
|
public AuthenticationProviderCacheResolver(String providerId, String userServiceId) {
|
||||||
|
this.providerId = providerId;
|
||||||
|
this.userServiceId = userServiceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
||||||
|
RootBeanDefinition provider = (RootBeanDefinition) beanFactory.getBeanDefinition(providerId);
|
||||||
|
|
||||||
|
String cachingId = userServiceId + AbstractUserDetailsServiceBeanDefinitionParser.CACHING_SUFFIX;
|
||||||
|
|
||||||
|
if (beanFactory.containsBeanDefinition(cachingId)) {
|
||||||
|
RootBeanDefinition cachingUserService = (RootBeanDefinition) beanFactory.getBeanDefinition(cachingId);
|
||||||
|
|
||||||
|
PropertyValue userCacheProperty = cachingUserService.getPropertyValues().getPropertyValue("userCache");
|
||||||
|
|
||||||
|
provider.getPropertyValues().addPropertyValue(userCacheProperty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOrder() {
|
||||||
|
return HIGHEST_PRECEDENCE;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
package org.springframework.security.config;
|
||||||
|
|
||||||
|
import org.springframework.security.providers.dao.UserCache;
|
||||||
|
import org.springframework.security.providers.dao.cache.NullUserCache;
|
||||||
|
import org.springframework.security.userdetails.UserDetailsService;
|
||||||
|
import org.springframework.security.userdetails.UserDetails;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Luke Taylor
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
class CachingUserDetailsService implements UserDetailsService {
|
||||||
|
private UserCache userCache = new NullUserCache();
|
||||||
|
private UserDetailsService delegate;
|
||||||
|
|
||||||
|
CachingUserDetailsService(UserDetailsService delegate) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserCache getUserCache() {
|
||||||
|
return userCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserCache(UserCache userCache) {
|
||||||
|
this.userCache = userCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserDetails loadUserByUsername(String username) {
|
||||||
|
UserDetails user = userCache.getUserFromCache(username);
|
||||||
|
|
||||||
|
if (user == null) {
|
||||||
|
user = delegate.loadUserByUsername(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.notNull(user, "UserDetailsService " + delegate + " returned null for username " + username + ". " +
|
||||||
|
"This is an interface contract violation");
|
||||||
|
|
||||||
|
userCache.putUserInCache(user);
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
|
@ -74,8 +74,16 @@ public abstract class ConfigUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains a user details service for use in RememberMeServices etc. Will return a caching version
|
||||||
|
* if available so should not be used for beans which need to separate the two.
|
||||||
|
*/
|
||||||
static UserDetailsService getUserDetailsService(ConfigurableListableBeanFactory bf) {
|
static UserDetailsService getUserDetailsService(ConfigurableListableBeanFactory bf) {
|
||||||
Map services = bf.getBeansOfType(UserDetailsService.class);
|
Map services = bf.getBeansOfType(CachingUserDetailsService.class);
|
||||||
|
|
||||||
|
if (services.size() == 0) {
|
||||||
|
services = bf.getBeansOfType(UserDetailsService.class);
|
||||||
|
}
|
||||||
|
|
||||||
if (services.size() == 0) {
|
if (services.size() == 0) {
|
||||||
throw new IllegalArgumentException("No UserDetailsService registered.");
|
throw new IllegalArgumentException("No UserDetailsService registered.");
|
||||||
|
|
|
@ -260,7 +260,9 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
|
||||||
registry.registerBeanDefinition(BeanIds.FILTER_SECURITY_INTERCEPTOR, filterSecurityInterceptorBuilder.getBeanDefinition());
|
registry.registerBeanDefinition(BeanIds.FILTER_SECURITY_INTERCEPTOR, filterSecurityInterceptorBuilder.getBeanDefinition());
|
||||||
|
|
||||||
// Register the post processor which will tie up the loose ends in the configuration once the app context has been created and all beans are available.
|
// Register the post processor which will tie up the loose ends in the configuration once the app context has been created and all beans are available.
|
||||||
registry.registerBeanDefinition(BeanIds.HTTP_POST_PROCESSOR, new RootBeanDefinition(HttpSecurityConfigPostProcessor.class));
|
RootBeanDefinition postProcessor = new RootBeanDefinition(HttpSecurityConfigPostProcessor.class);
|
||||||
|
postProcessor.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||||
|
registry.registerBeanDefinition(BeanIds.HTTP_POST_PROCESSOR, postProcessor);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,8 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||||
|
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||||
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
import org.springframework.core.OrderComparator;
|
import org.springframework.core.OrderComparator;
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
import org.springframework.security.concurrent.ConcurrentSessionFilter;
|
import org.springframework.security.concurrent.ConcurrentSessionFilter;
|
||||||
|
@ -52,30 +54,46 @@ public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor
|
||||||
configureFilterChain(beanFactory);
|
configureFilterChain(beanFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void injectUserDetailsServiceIntoRememberMeServices(ConfigurableListableBeanFactory beanFactory) {
|
private void injectUserDetailsServiceIntoRememberMeServices(ConfigurableListableBeanFactory bf) {
|
||||||
try {
|
try {
|
||||||
BeanDefinition rememberMeServices = beanFactory.getBeanDefinition(BeanIds.REMEMBER_ME_SERVICES);
|
BeanDefinition rememberMeServices = bf.getBeanDefinition(BeanIds.REMEMBER_ME_SERVICES);
|
||||||
PropertyValue pv = rememberMeServices.getPropertyValues().getPropertyValue("userDetailsService");
|
PropertyValue pv = rememberMeServices.getPropertyValues().getPropertyValue("userDetailsService");
|
||||||
|
|
||||||
if (pv == null) {
|
if (pv == null) {
|
||||||
rememberMeServices.getPropertyValues().addPropertyValue("userDetailsService",
|
rememberMeServices.getPropertyValues().addPropertyValue("userDetailsService",
|
||||||
ConfigUtils.getUserDetailsService(beanFactory));
|
ConfigUtils.getUserDetailsService(bf));
|
||||||
|
} else {
|
||||||
|
RuntimeBeanReference cachingUserService = getCachingUserService(bf, pv.getValue());
|
||||||
|
|
||||||
|
if (cachingUserService != null) {
|
||||||
|
rememberMeServices.getPropertyValues().addPropertyValue("userDetailsService", cachingUserService);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (NoSuchBeanDefinitionException e) {
|
} catch (NoSuchBeanDefinitionException e) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void injectUserDetailsServiceIntoX509Provider(ConfigurableListableBeanFactory beanFactory) {
|
private void injectUserDetailsServiceIntoX509Provider(ConfigurableListableBeanFactory bf) {
|
||||||
try {
|
try {
|
||||||
BeanDefinition x509AuthProvider = beanFactory.getBeanDefinition(BeanIds.X509_AUTH_PROVIDER);
|
BeanDefinition x509AuthProvider = bf.getBeanDefinition(BeanIds.X509_AUTH_PROVIDER);
|
||||||
PropertyValue pv = x509AuthProvider.getPropertyValues().getPropertyValue("preAuthenticatedUserDetailsService");
|
PropertyValue pv = x509AuthProvider.getPropertyValues().getPropertyValue("preAuthenticatedUserDetailsService");
|
||||||
|
|
||||||
if (pv == null) {
|
if (pv == null) {
|
||||||
UserDetailsByNameServiceWrapper preAuthUserService = new UserDetailsByNameServiceWrapper();
|
UserDetailsByNameServiceWrapper preAuthUserService = new UserDetailsByNameServiceWrapper();
|
||||||
preAuthUserService.setUserDetailsService(ConfigUtils.getUserDetailsService(beanFactory));
|
preAuthUserService.setUserDetailsService(ConfigUtils.getUserDetailsService(bf));
|
||||||
x509AuthProvider.getPropertyValues().addPropertyValue("preAuthenticatedUserDetailsService",
|
x509AuthProvider.getPropertyValues().addPropertyValue("preAuthenticatedUserDetailsService",
|
||||||
preAuthUserService);
|
preAuthUserService);
|
||||||
|
} else {
|
||||||
|
RootBeanDefinition preAuthUserService = (RootBeanDefinition) pv.getValue();
|
||||||
|
Object userService =
|
||||||
|
preAuthUserService.getPropertyValues().getPropertyValue("userDetailsService").getValue();
|
||||||
|
|
||||||
|
RuntimeBeanReference cachingUserService = getCachingUserService(bf, userService);
|
||||||
|
|
||||||
|
if (cachingUserService != null) {
|
||||||
|
preAuthUserService.getPropertyValues().addPropertyValue("userDetailsService", cachingUserService);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (NoSuchBeanDefinitionException e) {
|
} catch (NoSuchBeanDefinitionException e) {
|
||||||
// ignore
|
// ignore
|
||||||
|
@ -94,7 +112,22 @@ public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor
|
||||||
} catch (NoSuchBeanDefinitionException e) {
|
} catch (NoSuchBeanDefinitionException e) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private RuntimeBeanReference getCachingUserService(ConfigurableListableBeanFactory bf, Object userServiceRef) {
|
||||||
|
Assert.isInstanceOf(RuntimeBeanReference.class, userServiceRef,
|
||||||
|
"userDetailsService property value must be a RuntimeBeanReference");
|
||||||
|
|
||||||
|
String id = ((RuntimeBeanReference)userServiceRef).getBeanName();
|
||||||
|
// Overwrite with the caching version if available
|
||||||
|
String cachingId = id + AbstractUserDetailsServiceBeanDefinitionParser.CACHING_SUFFIX;
|
||||||
|
|
||||||
|
if (bf.containsBeanDefinition(cachingId)) {
|
||||||
|
return new RuntimeBeanReference(cachingId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the authentication manager, (and remember-me services, if required) on any instances of
|
* Sets the authentication manager, (and remember-me services, if required) on any instances of
|
||||||
|
@ -148,7 +181,7 @@ public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor
|
||||||
* <ol>
|
* <ol>
|
||||||
* <li>If only one, use that one.</li>
|
* <li>If only one, use that one.</li>
|
||||||
* <li>If more than one, use the form login entry point (if form login is being used), then try basic</li>
|
* <li>If more than one, use the form login entry point (if form login is being used), then try basic</li>
|
||||||
* <li>If still null, throw an exception (for now). TODO: Examine additional beans and types and make decision</li>
|
* <li>If still null, throw an exception (for now).</li>
|
||||||
* </ol>
|
* </ol>
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@ -257,6 +290,6 @@ public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getOrder() {
|
public int getOrder() {
|
||||||
return HIGHEST_PRECEDENCE;
|
return HIGHEST_PRECEDENCE + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package org.springframework.security.config;
|
||||||
|
|
||||||
import org.springframework.security.userdetails.jdbc.JdbcUserDetailsManager;
|
import org.springframework.security.userdetails.jdbc.JdbcUserDetailsManager;
|
||||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||||
|
import org.springframework.beans.factory.xml.ParserContext;
|
||||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||||
|
|
||||||
import org.w3c.dom.Element;
|
import org.w3c.dom.Element;
|
||||||
|
@ -17,7 +18,7 @@ public class JdbcUserServiceBeanDefinitionParser extends AbstractUserDetailsServ
|
||||||
return JdbcUserDetailsManager.class;
|
return JdbcUserDetailsManager.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void doParse(Element element, BeanDefinitionBuilder builder) {
|
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
|
||||||
// TODO: Set authenticationManager property
|
// TODO: Set authenticationManager property
|
||||||
String dataSource = element.getAttribute(ATT_DATA_SOURCE);
|
String dataSource = element.getAttribute(ATT_DATA_SOURCE);
|
||||||
// An explicit dataSource was specified, so use it
|
// An explicit dataSource was specified, so use it
|
||||||
|
|
|
@ -4,6 +4,7 @@ import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
import org.springframework.beans.factory.config.PropertiesFactoryBean;
|
import org.springframework.beans.factory.config.PropertiesFactoryBean;
|
||||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
|
import org.springframework.beans.factory.xml.ParserContext;
|
||||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||||
import org.springframework.security.userdetails.memory.InMemoryDaoImpl;
|
import org.springframework.security.userdetails.memory.InMemoryDaoImpl;
|
||||||
import org.springframework.security.userdetails.memory.UserMap;
|
import org.springframework.security.userdetails.memory.UserMap;
|
||||||
|
@ -36,7 +37,7 @@ public class UserServiceBeanDefinitionParser extends AbstractUserDetailsServiceB
|
||||||
return InMemoryDaoImpl.class;
|
return InMemoryDaoImpl.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void doParse(Element element, BeanDefinitionBuilder builder) {
|
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
|
||||||
String userProperties = element.getAttribute(ATT_PROPERTIES);
|
String userProperties = element.getAttribute(ATT_PROPERTIES);
|
||||||
List userElts = DomUtils.getChildElementsByTagName(element, ELT_USER);
|
List userElts = DomUtils.getChildElementsByTagName(element, ELT_USER);
|
||||||
|
|
||||||
|
|
|
@ -1,66 +0,0 @@
|
||||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.springframework.security.providers;
|
|
||||||
|
|
||||||
import org.springframework.security.AuthenticationException;
|
|
||||||
import org.springframework.security.userdetails.UserDetails;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Populates the <code>UserDetails</code> associated with a CAS authenticated
|
|
||||||
* user.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* Intended to grant authorities (roles) for providers that do not support
|
|
||||||
* authorities/roles directly. It merely authenticates their identity.
|
|
||||||
* As Spring Security needs to know the authorities granted to a user in
|
|
||||||
* order to construct a valid <code>Authentication</code> object, implementations
|
|
||||||
* of this interface will provide this information.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* A {@link UserDetails} is returned by implementations. The
|
|
||||||
* <code>UserDetails</code> must, at minimum, contain the username and
|
|
||||||
* <code>GrantedAuthority[]</code> objects applicable to the authenticated
|
|
||||||
* user. Note that Spring Security ignores the password and enabled/disabled
|
|
||||||
* status of the <code>UserDetails</code> because this is
|
|
||||||
* authentication-related and should have been enforced by another provider server. The
|
|
||||||
* <code>UserDetails</code> returned by implementations is stored in the
|
|
||||||
* generated <code>AuthenticationToken</code>, so additional properties
|
|
||||||
* such as email addresses, telephone numbers etc can easily be stored.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* Implementations should not perform any caching. They will only be called
|
|
||||||
* when a refresh is required.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @author Ben Alex
|
|
||||||
* @author Ray Krueger
|
|
||||||
* @version $Id$
|
|
||||||
*/
|
|
||||||
public interface UserDetailsService {
|
|
||||||
/**
|
|
||||||
* Obtains the granted authorities for the specified user.<P>May throw any
|
|
||||||
* <code>AuthenticationException</code> or return <code>null</code> if the authorities are unavailable.</p>
|
|
||||||
*
|
|
||||||
* @param casUserId as obtained from the CAS validation service
|
|
||||||
*
|
|
||||||
* @return the details of the indicated user (at minimum the granted authorities and the username)
|
|
||||||
*
|
|
||||||
* @throws org.springframework.security.AuthenticationException DOCUMENT ME!
|
|
||||||
*/
|
|
||||||
UserDetails getUserDetails(String casUserId)
|
|
||||||
throws AuthenticationException;
|
|
||||||
}
|
|
|
@ -29,7 +29,11 @@ id =
|
||||||
ref =
|
ref =
|
||||||
## Defines a reference to a Spring bean Id.
|
## Defines a reference to a Spring bean Id.
|
||||||
attribute ref {xsd:string}
|
attribute ref {xsd:string}
|
||||||
|
|
||||||
|
cache-ref =
|
||||||
|
## Defines a reference to a cache for use with a UserDetailsService.
|
||||||
|
attribute cache-ref {xsd:string}
|
||||||
|
|
||||||
user-service-ref =
|
user-service-ref =
|
||||||
## A reference to a user-service (or UserDetailsService bean) Id
|
## A reference to a user-service (or UserDetailsService bean) Id
|
||||||
attribute user-service-ref {xsd:string}
|
attribute user-service-ref {xsd:string}
|
||||||
|
@ -104,6 +108,8 @@ ldap-us.attlist &=
|
||||||
group-search-base-attribute?
|
group-search-base-attribute?
|
||||||
ldap-us.attlist &=
|
ldap-us.attlist &=
|
||||||
group-role-attribute-attribute?
|
group-role-attribute-attribute?
|
||||||
|
ldap-us.attlist &=
|
||||||
|
cache-ref?
|
||||||
|
|
||||||
ldap-authentication-provider =
|
ldap-authentication-provider =
|
||||||
## Sets up an ldap authentication provider
|
## Sets up an ldap authentication provider
|
||||||
|
@ -391,7 +397,9 @@ jdbc-user-service =
|
||||||
jdbc-user-service.attlist &=
|
jdbc-user-service.attlist &=
|
||||||
## The bean ID of the DataSource which provides the required tables.
|
## The bean ID of the DataSource which provides the required tables.
|
||||||
attribute data-source-ref {xsd:string}
|
attribute data-source-ref {xsd:string}
|
||||||
|
jdbc-user-service.attlist &=
|
||||||
|
cache-ref?
|
||||||
|
|
||||||
any-user-service = user-service | jdbc-user-service | ldap-user-service
|
any-user-service = user-service | jdbc-user-service | ldap-user-service
|
||||||
|
|
||||||
custom-filter =
|
custom-filter =
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,16 +1,17 @@
|
||||||
package org.springframework.security.config;
|
package org.springframework.security.config;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertSame;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.security.AuthenticationManager;
|
||||||
|
import org.springframework.security.providers.ProviderManager;
|
||||||
|
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.providers.dao.DaoAuthenticationProvider;
|
||||||
import org.springframework.security.userdetails.jdbc.JdbcUserDetailsManager;
|
import org.springframework.security.userdetails.jdbc.JdbcUserDetailsManager;
|
||||||
import org.springframework.security.util.InMemoryXmlApplicationContext;
|
import org.springframework.security.util.InMemoryXmlApplicationContext;
|
||||||
import org.springframework.security.AuthenticationManager;
|
|
||||||
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
|
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Ben Alex
|
* @author Ben Alex
|
||||||
|
@ -18,7 +19,7 @@ import javax.sql.DataSource;
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
*/
|
*/
|
||||||
public class JdbcUserServiceBeanDefinitionParserTests {
|
public class JdbcUserServiceBeanDefinitionParserTests {
|
||||||
private InMemoryXmlApplicationContext appContext;
|
private static String USER_CACHE_XML = "<b:bean id='userCache' class='org.springframework.security.providers.dao.MockUserCache'/>";
|
||||||
|
|
||||||
private static String DATA_SOURCE =
|
private static String DATA_SOURCE =
|
||||||
" <b:bean id='populator' class='org.springframework.security.config.DataSourcePopulator'>" +
|
" <b:bean id='populator' class='org.springframework.security.config.DataSourcePopulator'>" +
|
||||||
|
@ -29,6 +30,8 @@ public class JdbcUserServiceBeanDefinitionParserTests {
|
||||||
" <b:constructor-arg value='jdbcnamespaces'/>" +
|
" <b:constructor-arg value='jdbcnamespaces'/>" +
|
||||||
" </b:bean>";
|
" </b:bean>";
|
||||||
|
|
||||||
|
private InMemoryXmlApplicationContext appContext;
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void closeAppContext() {
|
public void closeAppContext() {
|
||||||
if (appContext != null) {
|
if (appContext != null) {
|
||||||
|
@ -45,10 +48,20 @@ public class JdbcUserServiceBeanDefinitionParserTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void beanIdIsParsedCorrectly() {
|
public void beanIdIsParsedCorrectly() {
|
||||||
setContext("<jdbc-user-service id='customUserService' data-source-ref='dataSource'/>" + DATA_SOURCE);
|
setContext("<jdbc-user-service id='myUserService' data-source-ref='dataSource'/>" + DATA_SOURCE);
|
||||||
JdbcUserDetailsManager mgr = (JdbcUserDetailsManager) appContext.getBean("customUserService");
|
JdbcUserDetailsManager mgr = (JdbcUserDetailsManager) appContext.getBean("myUserService");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void cacheRefIsparsedCorrectly() {
|
||||||
|
setContext("<jdbc-user-service id='myUserService' cache-ref='userCache' data-source-ref='dataSource'/>"
|
||||||
|
+ DATA_SOURCE +USER_CACHE_XML);
|
||||||
|
JdbcUserDetailsManager mgr = (JdbcUserDetailsManager) appContext.getBean("myUserService");
|
||||||
|
CachingUserDetailsService cachingUserService =
|
||||||
|
(CachingUserDetailsService) appContext.getBean("myUserService" + AbstractUserDetailsServiceBeanDefinitionParser.CACHING_SUFFIX);
|
||||||
|
assertSame(cachingUserService.getUserCache(), appContext.getBean("userCache"));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void isSupportedByAuthenticationProviderElement() {
|
public void isSupportedByAuthenticationProviderElement() {
|
||||||
setContext(
|
setContext(
|
||||||
|
@ -59,6 +72,19 @@ public class JdbcUserServiceBeanDefinitionParserTests {
|
||||||
mgr.authenticate(new UsernamePasswordAuthenticationToken("rod", "koala"));
|
mgr.authenticate(new UsernamePasswordAuthenticationToken("rod", "koala"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void cacheIsInjectedIntoAuthenticationProvider() {
|
||||||
|
setContext(
|
||||||
|
"<authentication-provider>" +
|
||||||
|
" <jdbc-user-service cache-ref='userCache' data-source-ref='dataSource'/>" +
|
||||||
|
"</authentication-provider>" + DATA_SOURCE + USER_CACHE_XML);
|
||||||
|
ProviderManager mgr = (ProviderManager) appContext.getBean(BeanIds.AUTHENTICATION_MANAGER);
|
||||||
|
DaoAuthenticationProvider provider = (DaoAuthenticationProvider) mgr.getProviders().get(0);
|
||||||
|
assertSame(provider.getUserCache(), appContext.getBean("userCache"));
|
||||||
|
provider.authenticate(new UsernamePasswordAuthenticationToken("rod","koala"));
|
||||||
|
assertNotNull("Cache should contain user after authentication", provider.getUserCache().getUserFromCache("rod"));
|
||||||
|
}
|
||||||
|
|
||||||
private void setContext(String context) {
|
private void setContext(String context) {
|
||||||
appContext = new InMemoryXmlApplicationContext(context);
|
appContext = new InMemoryXmlApplicationContext(context);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue