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.
This commit is contained in:
parent
54882fe1ea
commit
67d5a5b814
|
@ -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";
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
@ -267,6 +273,11 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
|
|||
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 =
|
||||
|
|
|
@ -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,60 +120,12 @@ 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
|
||||
* 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.
|
||||
*
|
||||
* <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>
|
||||
* Todo: this could probably be more easily be done in a BeanPostProcessor for ExceptionTranslationFilter.
|
||||
*
|
||||
*/
|
||||
private void injectAuthenticationEntryPointIntoExceptionTranslationFilter(ConfigurableListableBeanFactory beanFactory) {
|
||||
|
@ -191,95 +133,21 @@ public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor
|
|||
|
||||
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");
|
||||
String entryPoint = null;
|
||||
|
||||
AuthenticationEntryPoint mainEntryPoint;
|
||||
|
||||
if (entryPoints.size() == 1) {
|
||||
mainEntryPoint = (AuthenticationEntryPoint) entryPoints.get(0);
|
||||
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");
|
||||
}
|
||||
}
|
||||
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("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);
|
||||
}
|
||||
|
||||
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() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -347,6 +347,26 @@ public class HttpSecurityBeanDefinitionParserTests {
|
|||
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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue