Bläddra i källkod

SEC-750: Support for JPA PersistenceContext annotation broken
http://jira.springframework.org/browse/SEC-750. Updates to prevent the HttpSecurityPostProcessor from causing beans to be instantiated. Added a simplified test case to HttpSecurityBeanDefinitionParserTests.

Luke Taylor 17 år sedan
förälder
incheckning
67d5a5b814

+ 2 - 0
core/src/main/java/org/springframework/security/config/BeanIds.java

@@ -17,6 +17,7 @@ public abstract class BeanIds {
 	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 FILTER_CHAIN_POST_PROCESSOR = "_filterChainProxyPostProcessor";
 
     public static final String JDBC_USER_DETAILS_MANAGER = "_jdbcUserDetailsManager";
 	public static final String USER_DETAILS_SERVICE = "_userDetailsService";
@@ -59,4 +60,5 @@ public abstract class BeanIds {
     public static final String X509_FILTER = "_x509ProcessingFilter";
     public static final String X509_AUTH_PROVIDER = "_x509AuthenitcationProvider";
     public static final String PRE_AUTH_ENTRY_POINT = "_preAuthenticatedProcessingFilterEntryPoint";
+    public static final String REMEMBER_ME_SERVICES_INJECTION_POST_PROCESSOR = "_rememberMeServicesInjectionBeanPostProcessor";
 }

+ 8 - 7
core/src/main/java/org/springframework/security/config/ConfigUtils.java

@@ -2,6 +2,7 @@ package org.springframework.security.config;
 
 import org.springframework.beans.factory.config.BeanDefinition;
 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.ManagedList;
 import org.springframework.beans.factory.support.RootBeanDefinition;
@@ -78,22 +79,22 @@ 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(CachingUserDetailsService.class);
+    static RuntimeBeanReference getUserDetailsService(ConfigurableListableBeanFactory bf) {
+        String[] services = bf.getBeanNamesForType(CachingUserDetailsService.class, false, false);
         
-        if (services.size() == 0) {
-        	services = bf.getBeansOfType(UserDetailsService.class);
+        if (services.length == 0) {
+        	services = bf.getBeanNamesForType(UserDetailsService.class);
         }
 
-        if (services.size() == 0) {
+        if (services.length == 0) {
             throw new IllegalArgumentException("No UserDetailsService registered.");
 
-        } else if (services.size() > 1) {
+        } else if (services.length > 1) {
             throw new IllegalArgumentException("More than one UserDetailsService registered. Please" +
                     "use a specific Id in your configuration");
         }
 
-        return (UserDetailsService) services.values().toArray()[0];
+        return new RuntimeBeanReference(services[0]);
     }
 
     private static AuthenticationManager getAuthenticationManager(ConfigurableListableBeanFactory bf) {

+ 98 - 0
core/src/main/java/org/springframework/security/config/FilterChainProxyPostProcessor.java

@@ -0,0 +1,98 @@
+package org.springframework.security.config;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.Filter;
+
+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.ListableBeanFactory;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.core.OrderComparator;
+import org.springframework.core.Ordered;
+import org.springframework.security.util.FilterChainProxy;
+import org.springframework.util.Assert;
+
+/**
+ * 
+ * @author Luke Taylor
+ * @version $Id$
+ * @since 2.0
+ */
+public class FilterChainProxyPostProcessor implements BeanPostProcessor, BeanFactoryAware {
+    private Log logger = LogFactory.getLog(getClass());
+    
+    private ListableBeanFactory beanFactory;    
+
+    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+        if(!beanName.equals(BeanIds.FILTER_CHAIN_PROXY)) {
+            return bean;
+        }
+        
+        FilterChainProxy filterChainProxy = (FilterChainProxy) bean;
+        // Set the default match
+        List defaultFilterChain = orderFilters(beanFactory);
+
+        // Note that this returns a copy
+        Map filterMap = filterChainProxy.getFilterChainMap();
+        String allUrlsMatch = filterChainProxy.getMatcher().getUniversalMatchPattern();
+
+        filterMap.put(allUrlsMatch, defaultFilterChain);
+        filterChainProxy.setFilterChainMap(filterMap);
+
+        logger.info("Configured filter chain(s): " + filterChainProxy);        
+
+        return bean;
+    }
+
+    private List orderFilters(ListableBeanFactory beanFactory) {
+        Map filters = beanFactory.getBeansOfType(Filter.class);
+
+        Assert.notEmpty(filters, "No filters found in app context!");
+
+        Iterator ids = filters.keySet().iterator();
+
+        List orderedFilters = new ArrayList();
+
+        while (ids.hasNext()) {
+            String id = (String) ids.next();
+            Filter filter = (Filter) filters.get(id);
+
+            if (filter instanceof FilterChainProxy) {
+                continue;
+            }
+
+            // Filters must be Spring security filters or wrapped using <custom-filter>
+            if (!filter.getClass().getName().startsWith("org.springframework.security")) {
+                continue;
+            }
+
+            if (!(filter instanceof Ordered)) {
+                logger.info("Filter " + id + " doesn't implement the Ordered interface, skipping it.");
+                continue;
+            }
+
+            orderedFilters.add(filter);
+        }
+
+        Collections.sort(orderedFilters, new OrderComparator());
+
+        return orderedFilters;
+    }    
+
+    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
+        return bean;
+    }
+
+    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
+        this.beanFactory = (ListableBeanFactory) beanFactory;
+    }
+
+}

+ 16 - 5
core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java

@@ -208,6 +208,8 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
         Element sessionControlElt = DomUtils.getChildElementByTagName(element, Elements.CONCURRENT_SESSIONS);
         if (sessionControlElt != null) {
             new ConcurrentSessionsBeanDefinitionParser().parse(sessionControlElt, parserContext);
+            logger.info("Concurrent session filter in use, setting 'forceEagerSessionCreation' to true");
+            httpScif.getPropertyValues().addPropertyValue("forceEagerSessionCreation", Boolean.TRUE);
         }
 
         String sessionFixationAttribute = element.getAttribute(ATT_SESSION_FIXATION_PROTECTION);
@@ -242,6 +244,10 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
         Element rememberMeElt = DomUtils.getChildElementByTagName(element, Elements.REMEMBER_ME);
         if (rememberMeElt != null || autoConfig) {
             new RememberMeBeanDefinitionParser().parse(rememberMeElt, parserContext);
+            // Post processor to inject RememberMeServices into filters which need it
+            RootBeanDefinition rememberMeInjectionPostProcessor = new RootBeanDefinition(RememberMeServicesInjectionBeanPostProcessor.class);
+            rememberMeInjectionPostProcessor.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
+            registry.registerBeanDefinition(BeanIds.REMEMBER_ME_SERVICES_INJECTION_POST_PROCESSOR, rememberMeInjectionPostProcessor);            
         }
         
         Element logoutElt = DomUtils.getChildElementByTagName(element, Elements.LOGOUT);
@@ -266,7 +272,12 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
         RootBeanDefinition postProcessor = new RootBeanDefinition(HttpSecurityConfigPostProcessor.class);
         postProcessor.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
         registry.registerBeanDefinition(BeanIds.HTTP_POST_PROCESSOR, postProcessor);
-
+        
+        // Post processor specifically to assemble and order the filter chain immediately before the FilterChainProxy is initialized.
+        RootBeanDefinition filterChainPostProcessor = new RootBeanDefinition(FilterChainProxyPostProcessor.class);
+        filterChainPostProcessor.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
+        registry.registerBeanDefinition(BeanIds.FILTER_CHAIN_POST_PROCESSOR, filterChainPostProcessor);
+        
         return null;
     }
     
@@ -326,22 +337,22 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
             parserContext.getRegistry().registerBeanDefinition(BeanIds.OPEN_ID_PROVIDER, openIDProvider);
         }
         
-        if (formLoginFilter == null && openIDFilter == null) {
-        	return;
-        }
+        boolean needLoginPage = false;
         
         if (formLoginFilter != null) {
+        	needLoginPage = true;
 	        parserContext.getRegistry().registerBeanDefinition(BeanIds.FORM_LOGIN_FILTER, formLoginFilter);
 	        parserContext.getRegistry().registerBeanDefinition(BeanIds.FORM_LOGIN_ENTRY_POINT, formLoginEntryPoint);
         }        
 
         if (openIDFilter != null) {
+        	needLoginPage = true;
 	        parserContext.getRegistry().registerBeanDefinition(BeanIds.OPEN_ID_FILTER, openIDFilter);
 	        parserContext.getRegistry().registerBeanDefinition(BeanIds.OPEN_ID_ENTRY_POINT, openIDEntryPoint);
         }
 
         // If no login page has been defined, add in the default page generator.
-        if (formLoginPage == null && openIDLoginPage == null) {
+        if (needLoginPage && formLoginPage == null && openIDLoginPage == null) {
             logger.info("No login page configured. The default internal one will be used. Use the '"
                      + FormLoginBeanDefinitionParser.ATT_LOGIN_PAGE + "' attribute to set the URL of the login page.");
             BeanDefinitionBuilder loginPageFilter = 

+ 30 - 162
core/src/main/java/org/springframework/security/config/HttpSecurityConfigPostProcessor.java

@@ -1,38 +1,33 @@
 package org.springframework.security.config;
 
-import java.util.ArrayList;
-import java.util.Collections;
 import java.util.Iterator;
-import java.util.List;
 import java.util.Map;
 
-import javax.servlet.Filter;
-
 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.OrderComparator;
 import org.springframework.core.Ordered;
-import org.springframework.security.concurrent.ConcurrentSessionFilter;
-import org.springframework.security.context.HttpSessionContextIntegrationFilter;
 import org.springframework.security.ui.AbstractProcessingFilter;
 import org.springframework.security.ui.AuthenticationEntryPoint;
 import org.springframework.security.ui.basicauth.BasicProcessingFilter;
 import org.springframework.security.ui.rememberme.RememberMeServices;
 import org.springframework.security.userdetails.UserDetailsByNameServiceWrapper;
-import org.springframework.security.util.FilterChainProxy;
 import org.springframework.util.Assert;
 
 /**
- * Responsible for tying up the HTTP security configuration - building ordered filter stack and linking up
- * with other beans.
+ * 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 approach should avoid any
+ * conflict with other processors.
  *
  * @author Luke Taylor
  * @author Ben Alex
@@ -46,12 +41,7 @@ public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor
         injectUserDetailsServiceIntoRememberMeServices(beanFactory);
         injectUserDetailsServiceIntoX509Provider(beanFactory);
         injectUserDetailsServiceIntoOpenIDProvider(beanFactory);
-
         injectAuthenticationEntryPointIntoExceptionTranslationFilter(beanFactory);
-
-        injectRememberMeServicesIntoFiltersRequiringIt(beanFactory);
-
-        configureFilterChain(beanFactory);
     }
 
     private void injectUserDetailsServiceIntoRememberMeServices(ConfigurableListableBeanFactory bf) {
@@ -80,10 +70,10 @@ public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor
             PropertyValue pv = x509AuthProvider.getPropertyValues().getPropertyValue("preAuthenticatedUserDetailsService");
 
             if (pv == null) {
-                UserDetailsByNameServiceWrapper preAuthUserService = new UserDetailsByNameServiceWrapper();
-                preAuthUserService.setUserDetailsService(ConfigUtils.getUserDetailsService(bf));
+            	BeanDefinitionBuilder preAuthUserService = BeanDefinitionBuilder.rootBeanDefinition(UserDetailsByNameServiceWrapper.class);
+            	preAuthUserService.addPropertyValue("userDetailsService", ConfigUtils.getUserDetailsService(bf));
                 x509AuthProvider.getPropertyValues().addPropertyValue("preAuthenticatedUserDetailsService",
-                        preAuthUserService);
+                        preAuthUserService.getBeanDefinition());
             } else {
             	RootBeanDefinition preAuthUserService = (RootBeanDefinition) pv.getValue();
             	Object userService = 
@@ -130,156 +120,34 @@ public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor
     }
 
     /**
-     * Sets the remember-me services, if required, on any instances of AbstractProcessingFilter and 
-     * BasicProcessingFilter.
-     */
-    private void injectRememberMeServicesIntoFiltersRequiringIt(ConfigurableListableBeanFactory beanFactory) {
-        Map beans = beanFactory.getBeansOfType(RememberMeServices.class);
-
-        RememberMeServices rememberMeServices = null;
-
-        if(beans.size() == 0) {
-            logger.debug("No RememberMeServices configured");
-            return;
-        }
-
-        if (beans.size() == 1) {
-            rememberMeServices = (RememberMeServices) beans.values().toArray()[0];
-        } else {
-            throw new SecurityConfigurationException("More than one RememberMeServices bean found.");
-        }
-        
-        if (rememberMeServices == null) {
-            return;
-        }
-
-        // Address AbstractProcessingFilter instances
-        Iterator filters = beanFactory.getBeansOfType(AbstractProcessingFilter.class).values().iterator();
-
-        while (filters.hasNext()) {
-            AbstractProcessingFilter filter = (AbstractProcessingFilter) filters.next();
-
-            logger.info("Using RememberMeServices " + rememberMeServices + " with filter " + filter);
-            filter.setRememberMeServices(rememberMeServices);
-        }
-
-        // Address BasicProcessingFilter instance, if it exists
-        // NB: For remember-me to be sent back, a user must submit a "_spring_security_remember_me" with their login request.
-        // Most of the time a user won't present such a parameter with their BASIC authentication request.
-        // In the future we might support setting the AbstractRememberMeServices.alwaysRemember = true, but I am reluctant to
-        // do so because it seems likely to lead to lower security for 99.99% of users if they set the property to true.
-        if (beanFactory.containsBean(BeanIds.BASIC_AUTHENTICATION_FILTER)) {
-            BasicProcessingFilter filter = (BasicProcessingFilter) beanFactory.getBean(BeanIds.BASIC_AUTHENTICATION_FILTER);
-
-            logger.info("Using RememberMeServices " + rememberMeServices + " with filter " + filter);
-            filter.setRememberMeServices(rememberMeServices);
-        }
-    }
-
-    /**
-     * Selects the entry point that should be used in ExceptionTranslationFilter. Strategy is
-     *
-     * <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).</li>
-     * </ol>
+     * 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");
-
+        logger.info("Selecting AuthenticationEntryPoint for use in ExceptionTranslationFilter");        
+        
         BeanDefinition etf =
                 beanFactory.getBeanDefinition(BeanIds.EXCEPTION_TRANSLATION_FILTER);
-        Map entryPointMap = beanFactory.getBeansOfType(AuthenticationEntryPoint.class);
-        List entryPoints = new ArrayList(entryPointMap.values());
-
-        Assert.isTrue(entryPoints.size() > 0, "No AuthenticationEntryPoint instances defined");
-
-        AuthenticationEntryPoint mainEntryPoint;
-
-        if (entryPoints.size() == 1) {
-            mainEntryPoint = (AuthenticationEntryPoint) entryPoints.get(0);
+        
+        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 {
-            mainEntryPoint = (AuthenticationEntryPoint) beanFactory.getBean(BeanIds.MAIN_ENTRY_POINT);
-            
-            if (mainEntryPoint == null) {
-            	mainEntryPoint = (AuthenticationEntryPoint) entryPointMap.get(BeanIds.FORM_LOGIN_ENTRY_POINT);
-            }
-
-            if (mainEntryPoint == null) {
-                mainEntryPoint = (AuthenticationEntryPoint) entryPointMap.get(BeanIds.BASIC_AUTHENTICATION_ENTRY_POINT);
-                if (mainEntryPoint == null) {
-                    throw new SecurityConfigurationException("Failed to resolve authentication entry point");
-                }
-            }
-        }
-
-        logger.info("Main AuthenticationEntryPoint set to " + mainEntryPoint);
-
-        etf.getPropertyValues().addPropertyValue("authenticationEntryPoint", mainEntryPoint);
-    }
-
-    private void configureFilterChain(ConfigurableListableBeanFactory beanFactory) {
-        FilterChainProxy filterChainProxy =
-                (FilterChainProxy) beanFactory.getBean(BeanIds.FILTER_CHAIN_PROXY);
-        // Set the default match
-        List defaultFilterChain = orderFilters(beanFactory);
-
-        // Note that this returns a copy
-        Map filterMap = filterChainProxy.getFilterChainMap();
-
-        String allUrlsMatch = filterChainProxy.getMatcher().getUniversalMatchPattern();
-
-        filterMap.put(allUrlsMatch, defaultFilterChain);
-
-        filterChainProxy.setFilterChainMap(filterMap);
-
-        Map sessionFilters = beanFactory.getBeansOfType(ConcurrentSessionFilter.class);
-
-        if (!sessionFilters.isEmpty()) {
-            logger.info("Concurrent session filter in use, setting 'forceEagerSessionCreation' to true");
-            HttpSessionContextIntegrationFilter scif = (HttpSessionContextIntegrationFilter)
-                    beanFactory.getBean(BeanIds.HTTP_SESSION_CONTEXT_INTEGRATION_FILTER);
-            scif.setForceEagerSessionCreation(true);
+            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("Configured filter chain(s): " + filterChainProxy);
-    }
-
-    private List orderFilters(ConfigurableListableBeanFactory beanFactory) {
-        Map filters = beanFactory.getBeansOfType(Filter.class);
-
-        Assert.notEmpty(filters, "No filters found in app context!");
-
-        Iterator ids = filters.keySet().iterator();
-
-        List orderedFilters = new ArrayList();
-
-        while (ids.hasNext()) {
-            String id = (String) ids.next();
-            Filter filter = (Filter) filters.get(id);
-
-            if (filter instanceof FilterChainProxy) {
-                continue;
-            }
-
-            // Filters must be Spring security filters or wrapped using <custom-filter>
-            if (!filter.getClass().getName().startsWith("org.springframework.security")) {
-                continue;
-            }
-
-            if (!(filter instanceof Ordered)) {
-                logger.info("Filter " + id + " doesn't implement the Ordered interface, skipping it.");
-                continue;
-            }
-
-            orderedFilters.add(filter);
-        }
-
-        Collections.sort(orderedFilters, new OrderComparator());
-
-        return orderedFilters;
+        
+        logger.info("Using bean '" + entryPoint + "' as the entry point.");
+        etf.getPropertyValues().addPropertyValue("authenticationEntryPoint", new RuntimeBeanReference(entryPoint));
     }
 
     public int getOrder() {

+ 66 - 0
core/src/main/java/org/springframework/security/config/RememberMeServicesInjectionBeanPostProcessor.java

@@ -0,0 +1,66 @@
+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.ListableBeanFactory;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.security.ui.AbstractProcessingFilter;
+import org.springframework.security.ui.basicauth.BasicProcessingFilter;
+import org.springframework.security.ui.rememberme.RememberMeServices;
+import org.springframework.util.Assert;
+
+/**
+ * 
+ * @author Luke Taylor
+ * @version $Id$
+ * @since 2.0
+ */
+public class RememberMeServicesInjectionBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
+    private Log logger = LogFactory.getLog(getClass());
+    
+    private ListableBeanFactory beanFactory;
+
+    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+        if (bean instanceof AbstractProcessingFilter) {
+            AbstractProcessingFilter pf = (AbstractProcessingFilter) bean;
+            
+            if (pf.getRememberMeServices() == null) {
+                logger.info("Setting RememberMeServices on bean " + beanName);
+                pf.setRememberMeServices(getRememberMeServices());
+            }
+        } else if (beanName.equals(BeanIds.BASIC_AUTHENTICATION_FILTER)) {
+            // NB: For remember-me to be sent back, a user must submit a "_spring_security_remember_me" with their login request.
+            // Most of the time a user won't present such a parameter with their BASIC authentication request.
+            // In the future we might support setting the AbstractRememberMeServices.alwaysRemember = true, but I am reluctant to
+            // do so because it seems likely to lead to lower security for 99.99% of users if they set the property to true.
+            
+            BasicProcessingFilter bf = (BasicProcessingFilter) bean;
+            logger.info("Setting RememberMeServices on bean " + beanName);            
+            bf.setRememberMeServices(getRememberMeServices());
+        }
+        
+        return bean;
+    }
+    
+    private RememberMeServices getRememberMeServices() {
+        Map beans = beanFactory.getBeansOfType(RememberMeServices.class);
+        
+        Assert.isTrue(beans.size() > 0, "No RememberMeServices configured"); 
+        Assert.isTrue(beans.size() == 1, "More than one RememberMeServices bean found.");
+
+        return (RememberMeServices) beans.values().toArray()[0];
+    }
+    
+    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
+        return bean;
+    }
+
+    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
+        this.beanFactory = (ListableBeanFactory) beanFactory;
+    }
+}

+ 21 - 1
core/src/test/java/org/springframework/security/config/HttpSecurityBeanDefinitionParserTests.java

@@ -345,7 +345,27 @@ public class HttpSecurityBeanDefinitionParserTests {
         List filters = getFilterChainProxy().getFilters("/someurl");
 
         assertFalse(filters.get(1) instanceof SessionFixationProtectionFilter);
-    }    
+    }
+    
+    /**
+     * See SEC-750. If the http security post processor causes beans to be instantiated too eagerly, they way miss
+     * additional processing. In this method we have a UserDetailsService which is referenced from the namespace
+     * and also has a post processor registered which will modify it.
+     */
+    @Test
+    public void httpElementDoesntInterfereWithBeanPostProcessing() {
+        setContext(
+                "<http auto-config='true'/>" +
+                "<authentication-provider user-service-ref='myUserService'/>" +
+                "<b:bean id='myUserService' class='org.springframework.security.config.PostProcessedMockUserDetailsService'/>" +
+                "<b:bean id='beanPostProcessor' class='org.springframework.security.config.MockUserServiceBeanPostProcessor'/>"
+        );
+
+        PostProcessedMockUserDetailsService service = (PostProcessedMockUserDetailsService)appContext.getBean("myUserService");
+
+        assertEquals("Hello from the post processor!", service.getPostProcessorWasHere());
+    }
+    
     
     private void setContext(String context) {
         appContext = new InMemoryXmlApplicationContext(context);

+ 25 - 0
core/src/test/java/org/springframework/security/config/MockUserServiceBeanPostProcessor.java

@@ -0,0 +1,25 @@
+package org.springframework.security.config;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+
+/**
+ * Test bean post processor which injects a message into a PostProcessedMockUserDetailsService.
+ * 
+ * @author Luke Taylor
+ * @version $Id$
+ */
+public class MockUserServiceBeanPostProcessor implements BeanPostProcessor {
+
+	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {		
+		return bean;
+	}
+
+	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+        if (bean instanceof PostProcessedMockUserDetailsService) {
+            ((PostProcessedMockUserDetailsService)bean).setPostProcessorWasHere("Hello from the post processor!");
+        }
+	    
+		return bean;
+	}
+}

+ 27 - 0
core/src/test/java/org/springframework/security/config/PostProcessedMockUserDetailsService.java

@@ -0,0 +1,27 @@
+package org.springframework.security.config;
+
+import org.springframework.dao.DataAccessException;
+import org.springframework.security.userdetails.UserDetails;
+import org.springframework.security.userdetails.UserDetailsService;
+import org.springframework.security.userdetails.UsernameNotFoundException;
+
+public class PostProcessedMockUserDetailsService implements UserDetailsService {
+	private String postProcessorWasHere;
+
+	public PostProcessedMockUserDetailsService() {
+        this.postProcessorWasHere = "Post processor hasn't been yet";
+    }
+
+    public String getPostProcessorWasHere() {
+		return postProcessorWasHere;
+	}
+
+	public void setPostProcessorWasHere(String postProcessorWasHere) {
+		this.postProcessorWasHere = postProcessorWasHere;
+	}
+
+	public UserDetails loadUserByUsername(String username)
+			throws UsernameNotFoundException, DataAccessException {
+		throw new UnsupportedOperationException("Not for actual use");
+	}
+}