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;
|
||||
|
||||
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.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
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.util.StringUtils;
|
||||
|
||||
|
@ -12,16 +16,53 @@ import org.w3c.dom.Element;
|
|||
* @author Luke Taylor
|
||||
* @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 {
|
||||
String id = super.resolveId(element, definition, parserContext);
|
||||
id = beanId;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext)
|
||||
throws BeanDefinitionStoreException {
|
||||
|
||||
String id = element.getAttribute("id");
|
||||
|
||||
if (StringUtils.hasText(id)) {
|
||||
return id;
|
||||
}
|
||||
|
||||
// If it's nested in a parent auth-provider, generate an id automatically
|
||||
if(Elements.AUTHENTICATION_PROVIDER.equals(element.getParentNode().getNodeName())) {
|
||||
return parserContext.getReaderContext().generateBeanName(definition);
|
||||
}
|
||||
|
@ -34,4 +75,8 @@ public class AbstractUserDetailsServiceBeanDefinitionParser extends AbstractSing
|
|||
|
||||
return BeanIds.USER_DETAILS_SERVICE;
|
||||
}
|
||||
|
||||
String getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,19 @@
|
|||
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.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
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.ParserContext;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.security.providers.dao.DaoAuthenticationProvider;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
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 jdbcUserServiceElt = DomUtils.getChildElementByTagName(element, Elements.JDBC_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) {
|
||||
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.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 {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
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) {
|
||||
throw new IllegalArgumentException("No UserDetailsService registered.");
|
||||
|
|
|
@ -260,7 +260,9 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
|
|||
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.
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
|||
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.support.RootBeanDefinition;
|
||||
import org.springframework.core.OrderComparator;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.security.concurrent.ConcurrentSessionFilter;
|
||||
|
@ -52,30 +54,46 @@ public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor
|
|||
configureFilterChain(beanFactory);
|
||||
}
|
||||
|
||||
private void injectUserDetailsServiceIntoRememberMeServices(ConfigurableListableBeanFactory beanFactory) {
|
||||
private void injectUserDetailsServiceIntoRememberMeServices(ConfigurableListableBeanFactory bf) {
|
||||
try {
|
||||
BeanDefinition rememberMeServices = beanFactory.getBeanDefinition(BeanIds.REMEMBER_ME_SERVICES);
|
||||
BeanDefinition rememberMeServices = bf.getBeanDefinition(BeanIds.REMEMBER_ME_SERVICES);
|
||||
PropertyValue pv = rememberMeServices.getPropertyValues().getPropertyValue("userDetailsService");
|
||||
|
||||
if (pv == null) {
|
||||
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) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
private void injectUserDetailsServiceIntoX509Provider(ConfigurableListableBeanFactory beanFactory) {
|
||||
private void injectUserDetailsServiceIntoX509Provider(ConfigurableListableBeanFactory bf) {
|
||||
try {
|
||||
BeanDefinition x509AuthProvider = beanFactory.getBeanDefinition(BeanIds.X509_AUTH_PROVIDER);
|
||||
BeanDefinition x509AuthProvider = bf.getBeanDefinition(BeanIds.X509_AUTH_PROVIDER);
|
||||
PropertyValue pv = x509AuthProvider.getPropertyValues().getPropertyValue("preAuthenticatedUserDetailsService");
|
||||
|
||||
if (pv == null) {
|
||||
UserDetailsByNameServiceWrapper preAuthUserService = new UserDetailsByNameServiceWrapper();
|
||||
preAuthUserService.setUserDetailsService(ConfigUtils.getUserDetailsService(beanFactory));
|
||||
preAuthUserService.setUserDetailsService(ConfigUtils.getUserDetailsService(bf));
|
||||
x509AuthProvider.getPropertyValues().addPropertyValue("preAuthenticatedUserDetailsService",
|
||||
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) {
|
||||
// ignore
|
||||
|
@ -94,7 +112,22 @@ public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor
|
|||
} catch (NoSuchBeanDefinitionException e) {
|
||||
// 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
|
||||
|
@ -148,7 +181,7 @@ public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor
|
|||
* <ol>
|
||||
* <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 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>
|
||||
*
|
||||
*/
|
||||
|
@ -257,6 +290,6 @@ public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor
|
|||
}
|
||||
|
||||
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.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
@ -17,7 +18,7 @@ public class JdbcUserServiceBeanDefinitionParser extends AbstractUserDetailsServ
|
|||
return JdbcUserDetailsManager.class;
|
||||
}
|
||||
|
||||
protected void doParse(Element element, BeanDefinitionBuilder builder) {
|
||||
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
|
||||
// TODO: Set authenticationManager property
|
||||
String dataSource = element.getAttribute(ATT_DATA_SOURCE);
|
||||
// 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.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.security.userdetails.memory.InMemoryDaoImpl;
|
||||
import org.springframework.security.userdetails.memory.UserMap;
|
||||
|
@ -36,7 +37,7 @@ public class UserServiceBeanDefinitionParser extends AbstractUserDetailsServiceB
|
|||
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);
|
||||
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 =
|
||||
## Defines a reference to a Spring bean Id.
|
||||
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 =
|
||||
## A reference to a user-service (or UserDetailsService bean) Id
|
||||
attribute user-service-ref {xsd:string}
|
||||
|
@ -104,6 +108,8 @@ ldap-us.attlist &=
|
|||
group-search-base-attribute?
|
||||
ldap-us.attlist &=
|
||||
group-role-attribute-attribute?
|
||||
ldap-us.attlist &=
|
||||
cache-ref?
|
||||
|
||||
ldap-authentication-provider =
|
||||
## Sets up an ldap authentication provider
|
||||
|
@ -391,7 +397,9 @@ jdbc-user-service =
|
|||
jdbc-user-service.attlist &=
|
||||
## The bean ID of the DataSource which provides the required tables.
|
||||
attribute data-source-ref {xsd:string}
|
||||
|
||||
jdbc-user-service.attlist &=
|
||||
cache-ref?
|
||||
|
||||
any-user-service = user-service | jdbc-user-service | ldap-user-service
|
||||
|
||||
custom-filter =
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,16 +1,17 @@
|
|||
package org.springframework.security.config;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
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.util.InMemoryXmlApplicationContext;
|
||||
import org.springframework.security.AuthenticationManager;
|
||||
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
/**
|
||||
* @author Ben Alex
|
||||
|
@ -18,7 +19,7 @@ import javax.sql.DataSource;
|
|||
* @version $Id$
|
||||
*/
|
||||
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 =
|
||||
" <b:bean id='populator' class='org.springframework.security.config.DataSourcePopulator'>" +
|
||||
|
@ -29,6 +30,8 @@ public class JdbcUserServiceBeanDefinitionParserTests {
|
|||
" <b:constructor-arg value='jdbcnamespaces'/>" +
|
||||
" </b:bean>";
|
||||
|
||||
private InMemoryXmlApplicationContext appContext;
|
||||
|
||||
@After
|
||||
public void closeAppContext() {
|
||||
if (appContext != null) {
|
||||
|
@ -45,10 +48,20 @@ public class JdbcUserServiceBeanDefinitionParserTests {
|
|||
|
||||
@Test
|
||||
public void beanIdIsParsedCorrectly() {
|
||||
setContext("<jdbc-user-service id='customUserService' data-source-ref='dataSource'/>" + DATA_SOURCE);
|
||||
JdbcUserDetailsManager mgr = (JdbcUserDetailsManager) appContext.getBean("customUserService");
|
||||
setContext("<jdbc-user-service id='myUserService' data-source-ref='dataSource'/>" + DATA_SOURCE);
|
||||
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
|
||||
public void isSupportedByAuthenticationProviderElement() {
|
||||
setContext(
|
||||
|
@ -59,6 +72,19 @@ public class JdbcUserServiceBeanDefinitionParserTests {
|
|||
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) {
|
||||
appContext = new InMemoryXmlApplicationContext(context);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue