mirror of
				https://github.com/spring-projects/spring-security.git
				synced 2025-10-31 06:38:42 +00:00 
			
		
		
		
	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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user