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:
Luke Taylor 2008-03-28 17:55:12 +00:00
parent db6fafaf56
commit 1463b9769d
12 changed files with 1454 additions and 2158 deletions

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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.");

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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 =

View File

@ -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);
}