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:
Luke Taylor 2008-04-06 00:04:50 +00:00
parent 54882fe1ea
commit 67d5a5b814
9 changed files with 293 additions and 175 deletions

View File

@ -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";
}

View File

@ -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) {

View File

@ -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;
}
}

View File

@ -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 =

View File

@ -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);
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];
}
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);
}
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() {

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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");
}
}