diff --git a/core/src/main/java/org/springframework/security/config/BeanIds.java b/core/src/main/java/org/springframework/security/config/BeanIds.java index 347589d8b3..7b029581d2 100644 --- a/core/src/main/java/org/springframework/security/config/BeanIds.java +++ b/core/src/main/java/org/springframework/security/config/BeanIds.java @@ -1,7 +1,5 @@ package org.springframework.security.config; -import org.springframework.beans.factory.config.BeanDefinition; - /** * Contains all the default Bean IDs created by the namespace support in Spring Security 2. *
@@ -18,7 +16,8 @@ public abstract class BeanIds { /** Package protected as end users shouldn't really be using this BFPP directly */ static final String INTERCEPT_METHODS_BEAN_FACTORY_POST_PROCESSOR = "_interceptMethodsBeanfactoryPP"; static final String CONTEXT_SOURCE_SETTING_POST_PROCESSOR = "_contextSettingPostProcessor"; - static final String HTTP_POST_PROCESSOR = "_httpConfigBeanFactoryPostProcessor"; + static final String ENTRY_POINT_INJECTION_POST_PROCESSOR = "_entryPointInjectionBeanPostProcessor"; + static final String USER_DETAILS_SERVICE_INJECTION_POST_PROCESSOR = "_userServiceInjectionPostProcessor"; static final String FILTER_CHAIN_POST_PROCESSOR = "_filterChainProxyPostProcessor"; static final String FILTER_LIST = "_filterChainList"; @@ -63,7 +62,7 @@ public abstract class BeanIds { public static final String CONTEXT_SOURCE = "_securityContextSource"; public static final String PORT_MAPPER = "_portMapper"; public static final String X509_FILTER = "_x509ProcessingFilter"; - public static final String X509_AUTH_PROVIDER = "_x509AuthenitcationProvider"; + public static final String X509_AUTH_PROVIDER = "_x509AuthenticationProvider"; public static final String PRE_AUTH_ENTRY_POINT = "_preAuthenticatedProcessingFilterEntryPoint"; public static final String REMEMBER_ME_SERVICES_INJECTION_POST_PROCESSOR = "_rememberMeServicesInjectionBeanPostProcessor"; diff --git a/core/src/main/java/org/springframework/security/config/ConfigUtils.java b/core/src/main/java/org/springframework/security/config/ConfigUtils.java index c40fecefa8..0ca3a48a71 100644 --- a/core/src/main/java/org/springframework/security/config/ConfigUtils.java +++ b/core/src/main/java/org/springframework/security/config/ConfigUtils.java @@ -1,6 +1,7 @@ package org.springframework.security.config; import java.util.List; +import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -89,28 +90,6 @@ public abstract class ConfigUtils { return authManager; } - /** - * 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 RuntimeBeanReference getUserDetailsService(ConfigurableListableBeanFactory bf) { - String[] services = bf.getBeanNamesForType(CachingUserDetailsService.class, false, false); - - if (services.length == 0) { - services = bf.getBeanNamesForType(UserDetailsService.class); - } - - if (services.length == 0) { - throw new IllegalArgumentException("No UserDetailsService registered."); - - } else if (services.length > 1) { - throw new IllegalArgumentException("More than one UserDetailsService registered. Please " + - "use a specific Id in your configuration"); - } - - return new RuntimeBeanReference(services[0]); - } - static ManagedList getRegisteredProviders(ParserContext parserContext) { BeanDefinition authManager = registerProviderManagerIfNecessary(parserContext); return (ManagedList) authManager.getPropertyValues().getPropertyValue("providers").getValue(); diff --git a/core/src/main/java/org/springframework/security/config/EntryPointInjectionBeanPostProcessor.java b/core/src/main/java/org/springframework/security/config/EntryPointInjectionBeanPostProcessor.java new file mode 100644 index 0000000000..a65486b60f --- /dev/null +++ b/core/src/main/java/org/springframework/security/config/EntryPointInjectionBeanPostProcessor.java @@ -0,0 +1,59 @@ +package org.springframework.security.config; + +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.security.ui.AuthenticationEntryPoint; +import org.springframework.security.ui.ExceptionTranslationFilter; +import org.springframework.util.Assert; + +/** + * + * @author Luke Taylor + * @since 2.0.2 + */ +public class EntryPointInjectionBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware { + private final Log logger = LogFactory.getLog(getClass()); + private ConfigurableListableBeanFactory beanFactory; + + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + if (!BeanIds.EXCEPTION_TRANSLATION_FILTER.equals(beanName)) { + return bean; + } + + logger.info("Selecting AuthenticationEntryPoint for use in ExceptionTranslationFilter"); + + ExceptionTranslationFilter etf = (ExceptionTranslationFilter) beanFactory.getBean(BeanIds.EXCEPTION_TRANSLATION_FILTER); + + Object entryPoint = null; + + if (beanFactory.containsBean(BeanIds.MAIN_ENTRY_POINT)) { + entryPoint = beanFactory.getBean(BeanIds.MAIN_ENTRY_POINT); + logger.info("Using main configured AuthenticationEntryPoint."); + } else { + Map entryPoints = beanFactory.getBeansOfType(AuthenticationEntryPoint.class); + Assert.isTrue(entryPoints.size() != 0, "No AuthenticationEntryPoint instances defined"); + Assert.isTrue(entryPoints.size() == 1, "More than one AuthenticationEntryPoint defined in context"); + entryPoint = entryPoints.values().toArray()[0]; + } + + logger.info("Using bean '" + entryPoint + "' as the entry point."); + etf.setAuthenticationEntryPoint((AuthenticationEntryPoint) entryPoint); + + return bean; + } + + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + return bean; + } + + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = (ConfigurableListableBeanFactory) beanFactory; + } +} diff --git a/core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java b/core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java index e8f4d26955..ffae1f3685 100644 --- a/core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java +++ b/core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java @@ -181,11 +181,14 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser { new X509BeanDefinitionParser().parse(x509Elt, parserContext); } - // 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. - RootBeanDefinition postProcessor = new RootBeanDefinition(HttpSecurityConfigPostProcessor.class); + // Register the post processors which will tie up the loose ends in the configuration once the app context has been created and all beans are available. + RootBeanDefinition postProcessor = new RootBeanDefinition(EntryPointInjectionBeanPostProcessor.class); postProcessor.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); - registry.registerBeanDefinition(BeanIds.HTTP_POST_PROCESSOR, postProcessor); - + registry.registerBeanDefinition(BeanIds.ENTRY_POINT_INJECTION_POST_PROCESSOR, postProcessor); + RootBeanDefinition postProcessor2 = new RootBeanDefinition(UserDetailsServiceInjectionBeanPostProcessor.class); + postProcessor2.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + registry.registerBeanDefinition(BeanIds.USER_DETAILS_SERVICE_INJECTION_POST_PROCESSOR, postProcessor2); + return null; } diff --git a/core/src/main/java/org/springframework/security/config/HttpSecurityConfigPostProcessor.java b/core/src/main/java/org/springframework/security/config/HttpSecurityConfigPostProcessor.java deleted file mode 100644 index 90fc49b89b..0000000000 --- a/core/src/main/java/org/springframework/security/config/HttpSecurityConfigPostProcessor.java +++ /dev/null @@ -1,150 +0,0 @@ -package org.springframework.security.config; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.beans.BeansException; -import org.springframework.beans.PropertyValue; -import org.springframework.beans.factory.BeanFactory; -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.BeanDefinitionBuilder; -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.core.Ordered; -import org.springframework.security.ui.AuthenticationEntryPoint; -import org.springframework.security.userdetails.UserDetailsByNameServiceWrapper; -import org.springframework.util.Assert; - -/** - * Responsible for tying up the HTTP security configuration once all the beans are registered. - * This class does not actually instantiate any beans (for example, it should not call {@link BeanFactory#getBean(String)}). - * All the wiring up should be done using bean definitions or bean references to avoid this. This approach should avoid any - * conflict with other processors. - * - * @author Luke Taylor - * @author Ben Alex - * @version $Id$ - * @since 2.0 - */ -public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor, Ordered { - private Log logger = LogFactory.getLog(getClass()); - - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { - injectUserDetailsServiceIntoRememberMeServices(beanFactory); - injectUserDetailsServiceIntoX509Provider(beanFactory); - injectUserDetailsServiceIntoOpenIDProvider(beanFactory); - injectAuthenticationEntryPointIntoExceptionTranslationFilter(beanFactory); - } - - private void injectUserDetailsServiceIntoRememberMeServices(ConfigurableListableBeanFactory bf) { - try { - BeanDefinition rememberMeServices = bf.getBeanDefinition(BeanIds.REMEMBER_ME_SERVICES); - PropertyValue pv = rememberMeServices.getPropertyValues().getPropertyValue("userDetailsService"); - - if (pv == null) { - rememberMeServices.getPropertyValues().addPropertyValue("userDetailsService", - 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 bf) { - try { - BeanDefinition x509AuthProvider = bf.getBeanDefinition(BeanIds.X509_AUTH_PROVIDER); - PropertyValue pv = x509AuthProvider.getPropertyValues().getPropertyValue("preAuthenticatedUserDetailsService"); - - if (pv == null) { - BeanDefinitionBuilder preAuthUserService = BeanDefinitionBuilder.rootBeanDefinition(UserDetailsByNameServiceWrapper.class); - preAuthUserService.addPropertyValue("userDetailsService", ConfigUtils.getUserDetailsService(bf)); - x509AuthProvider.getPropertyValues().addPropertyValue("preAuthenticatedUserDetailsService", - preAuthUserService.getBeanDefinition()); - } 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 - } - } - - private void injectUserDetailsServiceIntoOpenIDProvider(ConfigurableListableBeanFactory beanFactory) { - try { - BeanDefinition openIDProvider = beanFactory.getBeanDefinition(BeanIds.OPEN_ID_PROVIDER); - PropertyValue pv = openIDProvider.getPropertyValues().getPropertyValue("userDetailsService"); - - if (pv == null) { - openIDProvider.getPropertyValues().addPropertyValue("userDetailsService", - ConfigUtils.getUserDetailsService(beanFactory)); - } - } 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; - } - - /** - * Selects the entry point that should be used in ExceptionTranslationFilter. If an entry point has been - * set during parsing of form, openID and basic authentication information, or via a custom reference - * (using custom-entry-point, then that will be used. Otherwise there - * must be a single entry point bean and that will be used. - * - * Todo: this could probably be more easily be done in a BeanPostProcessor for ExceptionTranslationFilter. - * - */ - private void injectAuthenticationEntryPointIntoExceptionTranslationFilter(ConfigurableListableBeanFactory beanFactory) { - logger.info("Selecting AuthenticationEntryPoint for use in ExceptionTranslationFilter"); - - BeanDefinition etf = - beanFactory.getBeanDefinition(BeanIds.EXCEPTION_TRANSLATION_FILTER); - - String entryPoint = null; - - if (beanFactory.containsBean(BeanIds.MAIN_ENTRY_POINT)) { - entryPoint = BeanIds.MAIN_ENTRY_POINT; - logger.info("Using main configured AuthenticationEntryPoint set to " + BeanIds.MAIN_ENTRY_POINT); - } else { - String[] entryPoints = beanFactory.getBeanNamesForType(AuthenticationEntryPoint.class); - Assert.isTrue(entryPoints.length != 0, "No AuthenticationEntryPoint instances defined"); - Assert.isTrue(entryPoints.length == 1, "More than one AuthenticationEntryPoint defined in context"); - entryPoint = entryPoints[0]; - } - - logger.info("Using bean '" + entryPoint + "' as the entry point."); - etf.getPropertyValues().addPropertyValue("authenticationEntryPoint", new RuntimeBeanReference(entryPoint)); - } - - public int getOrder() { - return HIGHEST_PRECEDENCE + 1; - } -} diff --git a/core/src/main/java/org/springframework/security/config/UserDetailsServiceInjectionBeanPostProcessor.java b/core/src/main/java/org/springframework/security/config/UserDetailsServiceInjectionBeanPostProcessor.java new file mode 100644 index 0000000000..85be87698d --- /dev/null +++ b/core/src/main/java/org/springframework/security/config/UserDetailsServiceInjectionBeanPostProcessor.java @@ -0,0 +1,139 @@ +package org.springframework.security.config; + +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.BeanWrapperImpl; +import org.springframework.beans.BeansException; +import org.springframework.beans.PropertyValue; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.config.RuntimeBeanReference; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.security.providers.preauth.PreAuthenticatedAuthenticationProvider; +import org.springframework.security.ui.rememberme.AbstractRememberMeServices; +import org.springframework.security.userdetails.UserDetailsByNameServiceWrapper; +import org.springframework.security.userdetails.UserDetailsService; +import org.springframework.util.Assert; + +/** + * + * @author Luke Taylor + * @since 2.0.2 + */ +public class UserDetailsServiceInjectionBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware { + private final Log logger = LogFactory.getLog(getClass()); + private ConfigurableListableBeanFactory beanFactory; + + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + if (BeanIds.X509_AUTH_PROVIDER.equals(beanName)) { + injectUserDetailsServiceIntoX509Provider((PreAuthenticatedAuthenticationProvider) bean); + } else if (BeanIds.REMEMBER_ME_SERVICES.equals(beanName)) { + injectUserDetailsServiceIntoRememberMeServices((AbstractRememberMeServices)bean); + } else if (BeanIds.OPEN_ID_PROVIDER.equals(beanName)) { + injectUserDetailsServiceIntoOpenIDProvider(bean); + } + + return bean; + } + + + + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + return bean; + } + + private void injectUserDetailsServiceIntoRememberMeServices(AbstractRememberMeServices services) { + BeanDefinition beanDefinition = beanFactory.getBeanDefinition(BeanIds.REMEMBER_ME_SERVICES); + PropertyValue pv = beanDefinition.getPropertyValues().getPropertyValue("userDetailsService"); + + if (pv == null) { + services.setUserDetailsService(getUserDetailsService()); + } else { + UserDetailsService cachingUserService = getCachingUserService(pv.getValue()); + + if (cachingUserService != null) { + services.setUserDetailsService(cachingUserService); + } + } + } + + private void injectUserDetailsServiceIntoX509Provider(PreAuthenticatedAuthenticationProvider provider) { + BeanDefinition beanDefinition = beanFactory.getBeanDefinition(BeanIds.X509_AUTH_PROVIDER); + PropertyValue pv = beanDefinition.getPropertyValues().getPropertyValue("preAuthenticatedUserDetailsService"); + UserDetailsByNameServiceWrapper wrapper = new UserDetailsByNameServiceWrapper(); + + if (pv == null) { + wrapper.setUserDetailsService(getUserDetailsService()); + provider.setPreAuthenticatedUserDetailsService(wrapper); + } else { + RootBeanDefinition preAuthUserService = (RootBeanDefinition) pv.getValue(); + Object userService = + preAuthUserService.getPropertyValues().getPropertyValue("userDetailsService").getValue(); + + UserDetailsService cachingUserService = getCachingUserService(userService); + + if (cachingUserService != null) { + wrapper.setUserDetailsService(cachingUserService); + provider.setPreAuthenticatedUserDetailsService(wrapper); + } + } + } + + private void injectUserDetailsServiceIntoOpenIDProvider(Object bean) { + BeanDefinition beanDefinition = beanFactory.getBeanDefinition(BeanIds.OPEN_ID_PROVIDER); + PropertyValue pv = beanDefinition.getPropertyValues().getPropertyValue("userDetailsService"); + + if (pv == null) { + BeanWrapperImpl beanWrapper = new BeanWrapperImpl(bean); + beanWrapper.setPropertyValue("userDetailsService", getUserDetailsService()); + } + } + + + /** + * 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. + */ + UserDetailsService getUserDetailsService() { + Map beans = beanFactory.getBeansOfType(CachingUserDetailsService.class); + + if (beans.size() == 0) { + beans = beanFactory.getBeansOfType(UserDetailsService.class); + } + + if (beans.size() == 0) { + throw new SecurityConfigurationException("No UserDetailsService registered."); + + } else if (beans.size() > 1) { + throw new SecurityConfigurationException("More than one UserDetailsService registered. Please " + + "use a specific Id in your configuration"); + } + + return (UserDetailsService) beans.values().toArray()[0]; + } + + private UserDetailsService getCachingUserService(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 (beanFactory.containsBeanDefinition(cachingId)) { + return (UserDetailsService) beanFactory.getBean(cachingId); + } + + return null; + } + + + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = (ConfigurableListableBeanFactory) beanFactory; + } +} diff --git a/core/src/main/java/org/springframework/security/config/X509BeanDefinitionParser.java b/core/src/main/java/org/springframework/security/config/X509BeanDefinitionParser.java index 53b65cfe1e..2c2fbba132 100644 --- a/core/src/main/java/org/springframework/security/config/X509BeanDefinitionParser.java +++ b/core/src/main/java/org/springframework/security/config/X509BeanDefinitionParser.java @@ -45,8 +45,8 @@ public class X509BeanDefinitionParser implements BeanDefinitionParser { } BeanDefinition provider = new RootBeanDefinition(PreAuthenticatedAuthenticationProvider.class); - ConfigUtils.getRegisteredProviders(parserContext).add(provider); parserContext.getRegistry().registerBeanDefinition(BeanIds.X509_AUTH_PROVIDER, provider); + ConfigUtils.getRegisteredProviders(parserContext).add(new RuntimeBeanReference(BeanIds.X509_AUTH_PROVIDER)); String userServiceRef = element.getAttribute(ATT_USER_SERVICE_REF);