Sfoglia il codice sorgente

SEC-826: Support for JPA PersistenceContext annotation broken
http://jira.springframework.org/browse/SEC-826 Moved all injection post-processing to BeanPostProcessors (and deleted bean factory post-processor) to prevent early instantiation problems. Beas should now all be instantiated before the injection takes place.

Luke Taylor 17 anni fa
parent
commit
59543af4fb

+ 3 - 4
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.
  * <p>
@@ -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";
 

+ 1 - 22
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();

+ 59 - 0
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;
+	}
+}

+ 7 - 4
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;
     }
     

+ 0 - 150
core/src/main/java/org/springframework/security/config/HttpSecurityConfigPostProcessor.java

@@ -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 <tt>custom-entry-point</tt>, 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;
-    }
-}

+ 139 - 0
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;
+	}
+}

+ 1 - 1
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);