From 8e7c540b167250119296fee3cd5f04be9925a8b2 Mon Sep 17 00:00:00 2001 From: Ben Alex Date: Tue, 4 Dec 2007 10:35:08 +0000 Subject: [PATCH] General refactorings and improvements to namespace support, including autoDetect="true" attribute for element. --- .../config/AnonymousBeanDefinitionParser.java | 32 +++- ...sicAuthenticationBeanDefinitionParser.java | 33 ++-- ...FilterChainMapBeanDefinitionDecorator.java | 6 +- .../config/FormLoginBeanDefinitionParser.java | 23 ++- .../HttpSecurityBeanDefinitionParser.java | 149 ++++++++++-------- .../config/LogoutBeanDefinitionParser.java | 71 +++++---- .../RememberMeBeanDefinitionParser.java | 25 +-- .../security/config/spring-security-2.0.rnc | 50 +++--- .../security/config/spring-security-2.0.xsd | 69 +++++--- .../security/config/http-security.xml | 6 +- .../applicationContext-security-ns.xml | 20 ++- 11 files changed, 289 insertions(+), 195 deletions(-) diff --git a/core/src/main/java/org/springframework/security/config/AnonymousBeanDefinitionParser.java b/core/src/main/java/org/springframework/security/config/AnonymousBeanDefinitionParser.java index a822d19410..d39bb7c011 100644 --- a/core/src/main/java/org/springframework/security/config/AnonymousBeanDefinitionParser.java +++ b/core/src/main/java/org/springframework/security/config/AnonymousBeanDefinitionParser.java @@ -9,6 +9,7 @@ import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.security.providers.anonymous.AnonymousAuthenticationProvider; import org.springframework.security.providers.anonymous.AnonymousProcessingFilter; +import org.springframework.util.StringUtils; import org.w3c.dom.Element; /** @@ -17,17 +18,40 @@ import org.w3c.dom.Element; */ public class AnonymousBeanDefinitionParser implements BeanDefinitionParser { static final String ATT_KEY = "key"; + static final String DEF_KEY = "doesNotMatter"; + static final String ATT_USERNAME = "username"; + static final String DEF_USERNAME = "roleAnonymous"; + static final String ATT_GRANTED_AUTHORITY = "grantedAuthority"; + static final String DEF_GRANTED_AUTHORITY = "ROLE_ANONYMOUS"; + protected final Log logger = LogFactory.getLog(getClass()); public BeanDefinition parse(Element element, ParserContext parserContext) { - BeanDefinition filter = new RootBeanDefinition(AnonymousProcessingFilter.class); + String grantedAuthority = null; + String username = null; + String key = null; + + if (element != null) { + grantedAuthority = element.getAttribute(ATT_GRANTED_AUTHORITY); + username = element.getAttribute(ATT_USERNAME); + key = element.getAttribute(ATT_KEY); + } + + if (!StringUtils.hasText(grantedAuthority)) { + grantedAuthority = DEF_GRANTED_AUTHORITY; + } + + if (!StringUtils.hasText(username)) { + username = DEF_USERNAME; + } - String grantedAuthority = element.getAttribute(ATT_GRANTED_AUTHORITY); - String username = element.getAttribute(ATT_USERNAME); - String key = element.getAttribute(ATT_KEY); + if (!StringUtils.hasText(key)) { + key = DEF_KEY; + } + BeanDefinition filter = new RootBeanDefinition(AnonymousProcessingFilter.class); filter.getPropertyValues().addPropertyValue("userAttribute", username + "," + grantedAuthority); filter.getPropertyValues().addPropertyValue(ATT_KEY, key); diff --git a/core/src/main/java/org/springframework/security/config/BasicAuthenticationBeanDefinitionParser.java b/core/src/main/java/org/springframework/security/config/BasicAuthenticationBeanDefinitionParser.java index e7f7b4d988..89afbf3e26 100644 --- a/core/src/main/java/org/springframework/security/config/BasicAuthenticationBeanDefinitionParser.java +++ b/core/src/main/java/org/springframework/security/config/BasicAuthenticationBeanDefinitionParser.java @@ -26,21 +26,20 @@ public class BasicAuthenticationBeanDefinitionParser implements BeanDefinitionPa } public BeanDefinition parse(Element elt, ParserContext parserContext) { - BeanDefinitionBuilder filterBuilder = - BeanDefinitionBuilder.rootBeanDefinition(BasicProcessingFilter.class); - RootBeanDefinition entryPoint = new RootBeanDefinition(BasicProcessingFilterEntryPoint.class); - - entryPoint.getPropertyValues().addPropertyValue("realmName", realmName); - - filterBuilder.addPropertyValue("authenticationEntryPoint", entryPoint); - parserContext.getRegistry().registerBeanDefinition(BeanIds.BASIC_AUTHENTICATION_ENTRY_POINT, entryPoint); - - filterBuilder.addPropertyValue("authenticationManager", new RuntimeBeanReference(BeanIds.AUTHENTICATION_MANAGER)); - filterBuilder.addPropertyValue("authenticationEntryPoint", new RuntimeBeanReference(BeanIds.BASIC_AUTHENTICATION_ENTRY_POINT)); - - parserContext.getRegistry().registerBeanDefinition(BeanIds.BASIC_AUTHENTICATION_FILTER, - filterBuilder.getBeanDefinition()); - - return null; - } + BeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder.rootBeanDefinition(BasicProcessingFilter.class); + RootBeanDefinition entryPoint = new RootBeanDefinition(BasicProcessingFilterEntryPoint.class); + + entryPoint.getPropertyValues().addPropertyValue("realmName", realmName); + + filterBuilder.addPropertyValue("authenticationEntryPoint", entryPoint); + parserContext.getRegistry().registerBeanDefinition(BeanIds.BASIC_AUTHENTICATION_ENTRY_POINT, entryPoint); + + filterBuilder.addPropertyValue("authenticationManager", new RuntimeBeanReference(BeanIds.AUTHENTICATION_MANAGER)); + filterBuilder.addPropertyValue("authenticationEntryPoint", new RuntimeBeanReference(BeanIds.BASIC_AUTHENTICATION_ENTRY_POINT)); + + parserContext.getRegistry().registerBeanDefinition(BeanIds.BASIC_AUTHENTICATION_FILTER, + filterBuilder.getBeanDefinition()); + + return null; + } } diff --git a/core/src/main/java/org/springframework/security/config/FilterChainMapBeanDefinitionDecorator.java b/core/src/main/java/org/springframework/security/config/FilterChainMapBeanDefinitionDecorator.java index 8903b8e549..7df4caa8a4 100644 --- a/core/src/main/java/org/springframework/security/config/FilterChainMapBeanDefinitionDecorator.java +++ b/core/src/main/java/org/springframework/security/config/FilterChainMapBeanDefinitionDecorator.java @@ -29,9 +29,9 @@ class FilterChainMapBeanDefinitionDecorator implements BeanDefinitionDecorator { Map filterChainMap = new LinkedHashMap(); Element elt = (Element)node; - String pathType = elt.getAttribute(HttpSecurityBeanDefinitionParser.ATT_PATTERN_TYPE); + String pathType = elt.getAttribute(HttpSecurityBeanDefinitionParser.ATT_PATH_TYPE); - if (HttpSecurityBeanDefinitionParser.ATT_PATTERN_TYPE_REGEX.equals(pathType)) { + if (HttpSecurityBeanDefinitionParser.OPT_PATH_TYPE_REGEX.equals(pathType)) { filterChainProxy.getPropertyValues().addPropertyValue("matcher", new RegexUrlPathMatcher()); } @@ -46,7 +46,7 @@ class FilterChainMapBeanDefinitionDecorator implements BeanDefinitionDecorator { Assert.hasText(filters, "The attribute '" + HttpSecurityBeanDefinitionParser.ATT_FILTERS + "'must not be empty"); - if (filters.equals(HttpSecurityBeanDefinitionParser.NO_FILTERS_VALUE)) { + if (filters.equals(HttpSecurityBeanDefinitionParser.OPT_FILTERS_NONE)) { filterChainMap.put(path, Collections.EMPTY_LIST); } else { String[] filterBeanNames = StringUtils.tokenizeToStringArray(filters, ","); diff --git a/core/src/main/java/org/springframework/security/config/FormLoginBeanDefinitionParser.java b/core/src/main/java/org/springframework/security/config/FormLoginBeanDefinitionParser.java index 16b7d0ba0f..c64f2445a6 100644 --- a/core/src/main/java/org/springframework/security/config/FormLoginBeanDefinitionParser.java +++ b/core/src/main/java/org/springframework/security/config/FormLoginBeanDefinitionParser.java @@ -36,9 +36,21 @@ public class FormLoginBeanDefinitionParser implements BeanDefinitionParser { static final String DEF_FORM_LOGIN_AUTHENTICATION_FAILURE_URL = DefaultLoginPageGeneratingFilter.DEFAULT_LOGIN_PAGE_URL + "?" + DefaultLoginPageGeneratingFilter.ERROR_PARAMETER_NAME; public BeanDefinition parse(Element elt, ParserContext parserContext) { - ConfigUtils.registerProviderManagerIfNecessary(parserContext); + String loginUrl = null; + String defaultTargetUrl = null; + String authenticationFailureUrl = null; + String loginPage = null; + + if (elt != null) { + loginUrl = elt.getAttribute(ATT_LOGIN_URL); + defaultTargetUrl = elt.getAttribute(ATT_FORM_LOGIN_TARGET_URL); + authenticationFailureUrl = elt.getAttribute(ATT_FORM_LOGIN_AUTHENTICATION_FAILURE_URL); + loginPage = elt.getAttribute(ATT_LOGIN_PAGE); + } - BeanDefinition filterBean = createFilterBean(elt); + ConfigUtils.registerProviderManagerIfNecessary(parserContext); + + BeanDefinition filterBean = createFilterBean(loginUrl, defaultTargetUrl, authenticationFailureUrl); filterBean.getPropertyValues().addPropertyValue("authenticationManager", new RuntimeBeanReference(BeanIds.AUTHENTICATION_MANAGER)); @@ -46,7 +58,6 @@ public class FormLoginBeanDefinitionParser implements BeanDefinitionParser { BeanDefinitionBuilder entryPointBuilder = BeanDefinitionBuilder.rootBeanDefinition(AuthenticationProcessingFilterEntryPoint.class); - String loginPage = elt.getAttribute(ATT_LOGIN_PAGE); // If no login page has been defined, add in the default page generator. if (!StringUtils.hasText(loginPage)) { @@ -68,11 +79,10 @@ public class FormLoginBeanDefinitionParser implements BeanDefinitionParser { return null; } - private BeanDefinition createFilterBean(Element elt) { + private BeanDefinition createFilterBean(String loginUrl, String defaultTargetUrl, String authenticationFailureUrl) { BeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder.rootBeanDefinition(AuthenticationProcessingFilter.class); - String loginUrl = elt.getAttribute(ATT_LOGIN_URL); if (!StringUtils.hasText(loginUrl)) { loginUrl = DEF_LOGIN_URL; @@ -80,7 +90,6 @@ public class FormLoginBeanDefinitionParser implements BeanDefinitionParser { filterBuilder.addPropertyValue("filterProcessesUrl", loginUrl); - String defaultTargetUrl = elt.getAttribute(ATT_FORM_LOGIN_TARGET_URL); if (!StringUtils.hasText(defaultTargetUrl)) { defaultTargetUrl = DEF_FORM_LOGIN_TARGET_URL; @@ -88,8 +97,6 @@ public class FormLoginBeanDefinitionParser implements BeanDefinitionParser { filterBuilder.addPropertyValue("defaultTargetUrl", defaultTargetUrl); - String authenticationFailureUrl = elt.getAttribute(ATT_FORM_LOGIN_AUTHENTICATION_FAILURE_URL); - if (!StringUtils.hasText(authenticationFailureUrl)) { authenticationFailureUrl = DEF_FORM_LOGIN_AUTHENTICATION_FAILURE_URL; } diff --git a/core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java b/core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java index c4ed8cd450..e27d89e797 100644 --- a/core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java +++ b/core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java @@ -1,5 +1,19 @@ package org.springframework.security.config; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.RuntimeBeanReference; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.beans.factory.xml.BeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; import org.springframework.security.ConfigAttributeDefinition; import org.springframework.security.ConfigAttributeEditor; import org.springframework.security.context.HttpSessionContextIntegrationFilter; @@ -15,26 +29,11 @@ import org.springframework.security.securechannel.SecureChannelProcessor; import org.springframework.security.ui.ExceptionTranslationFilter; import org.springframework.security.util.FilterChainProxy; import org.springframework.security.util.RegexUrlPathMatcher; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.RuntimeBeanReference; -import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.beans.factory.xml.BeanDefinitionParser; -import org.springframework.beans.factory.xml.ParserContext; import org.springframework.util.Assert; import org.springframework.util.StringUtils; import org.springframework.util.xml.DomUtils; - import org.w3c.dom.Element; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - /** * Sets up HTTP security: filter stack and protected URLs. * @@ -48,54 +47,84 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser { static final String DEF_REALM = "Spring Security Application"; static final String ATT_PATH_PATTERN = "pattern"; - static final String ATT_PATTERN_TYPE = "pathType"; - static final String ATT_PATTERN_TYPE_REGEX = "regex"; + + static final String ATT_PATH_TYPE = "pathType"; + static final String DEF_PATH_TYPE_ANT = "ant"; + static final String OPT_PATH_TYPE_REGEX = "regex"; static final String ATT_FILTERS = "filters"; - static final String NO_FILTERS_VALUE = "none"; + static final String OPT_FILTERS_NONE = "none"; static final String ATT_ACCESS_CONFIG = "access"; static final String ATT_REQUIRES_CHANNEL = "requiresChannel"; + static final String ATT_CREATE_SESSION = "create-session"; + static final String DEF_CREATE_SESSION_IF_REQUIRED = "ifRequired"; + static final String OPT_CREATE_SESSION_ALWAYS = "always"; + static final String OPT_CREATE_SESSION_NEVER = "never"; + + static final String ATT_LOWERCASE_COMPARISONS = "lowercaseComparisons"; + static final String DEF_LOWERCASE_COMPARISONS = "true"; + + static final String ATT_AUTO_CONFIG = "autoConfig"; + static final String DEF_AUTO_CONFIG = "false"; + public BeanDefinition parse(Element element, ParserContext parserContext) { RootBeanDefinition filterChainProxy = new RootBeanDefinition(FilterChainProxy.class); - RootBeanDefinition httpSCIF = new RootBeanDefinition(HttpSessionContextIntegrationFilter.class); - - //TODO: Set session creation parameters based on session-creation attribute - + RootBeanDefinition httpScif = new RootBeanDefinition(HttpSessionContextIntegrationFilter.class); + + String createSession = element.getAttribute(ATT_CREATE_SESSION); + if (OPT_CREATE_SESSION_ALWAYS.equals(createSession)) { + httpScif.getPropertyValues().addPropertyValue("allowSessionCreation", Boolean.TRUE); + httpScif.getPropertyValues().addPropertyValue("forceEagerSessionCreation", Boolean.TRUE); + } else if (OPT_CREATE_SESSION_NEVER.equals(createSession)) { + httpScif.getPropertyValues().addPropertyValue("allowSessionCreation", Boolean.FALSE); + httpScif.getPropertyValues().addPropertyValue("forceEagerSessionCreation", Boolean.FALSE); + } else { + createSession = DEF_CREATE_SESSION_IF_REQUIRED; + httpScif.getPropertyValues().addPropertyValue("allowSessionCreation", Boolean.TRUE); + httpScif.getPropertyValues().addPropertyValue("forceEagerSessionCreation", Boolean.FALSE); + } + BeanDefinitionBuilder filterSecurityInterceptorBuilder = BeanDefinitionBuilder.rootBeanDefinition(FilterSecurityInterceptor.class); BeanDefinitionBuilder exceptionTranslationFilterBuilder = BeanDefinitionBuilder.rootBeanDefinition(ExceptionTranslationFilter.class); - // Autowire for entry point (for now) - // TODO: Examine entry point beans in post processing and pick the correct one - // i.e. form login or cas if defined, then any other non-basic, non-digest, then basic or digest - exceptionTranslationFilterBuilder.setAutowireMode(RootBeanDefinition.AUTOWIRE_BY_TYPE); - - // TODO: Get path type attribute and determine FilDefInvS class - Map filterChainMap = new LinkedHashMap(); - String patternType = element.getAttribute(ATT_PATTERN_TYPE); + String patternType = element.getAttribute(ATT_PATH_TYPE); + if (!StringUtils.hasText(patternType)) { + patternType = DEF_PATH_TYPE_ANT; + } FilterInvocationDefinitionMap interceptorFilterInvDefSource = new PathBasedFilterInvocationDefinitionMap(); FilterInvocationDefinitionMap channelFilterInvDefSource = new PathBasedFilterInvocationDefinitionMap(); - if (patternType.equals(ATT_PATTERN_TYPE_REGEX)) { + if (patternType.equals(OPT_PATH_TYPE_REGEX)) { filterChainProxy.getPropertyValues().addPropertyValue("matcher", new RegexUrlPathMatcher()); interceptorFilterInvDefSource = new RegExpBasedFilterInvocationDefinitionMap(); channelFilterInvDefSource = new RegExpBasedFilterInvocationDefinitionMap(); } + // Deal with lowercase conversion requests + String lowercaseComparisons = element.getAttribute(ATT_LOWERCASE_COMPARISONS); + if (!StringUtils.hasText(lowercaseComparisons)) { + lowercaseComparisons = DEF_LOWERCASE_COMPARISONS; + } + if ("true".equals(lowercaseComparisons)) { + interceptorFilterInvDefSource.setConvertUrlToLowercaseBeforeComparison(true); + channelFilterInvDefSource.setConvertUrlToLowercaseBeforeComparison(true); + } else { + interceptorFilterInvDefSource.setConvertUrlToLowercaseBeforeComparison(false); + channelFilterInvDefSource.setConvertUrlToLowercaseBeforeComparison(false); + } + filterChainProxy.getPropertyValues().addPropertyValue("filterChainMap", filterChainMap); filterSecurityInterceptorBuilder.addPropertyValue("objectDefinitionSource", interceptorFilterInvDefSource); - // Again pick up auth manager - //filterSecurityInterceptorBuilder.setAutowireMode(RootBeanDefinition.AUTOWIRE_BY_TYPE); - parseInterceptUrls(DomUtils.getChildElementsByTagName(element, "intercept-url"), filterChainMap, interceptorFilterInvDefSource, channelFilterInvDefSource, parserContext); @@ -119,64 +148,56 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser { registry.registerBeanDefinition(BeanIds.CHANNEL_PROCESSING_FILTER, channelFilter); registry.registerBeanDefinition(BeanIds.CHANNEL_DECISION_MANAGER, channelDecisionManager); } + + String realm = element.getAttribute(ATT_REALM); + if (!StringUtils.hasText(realm)) { + realm = DEF_REALM; + } Element sessionControlElt = DomUtils.getChildElementByTagName(element, Elements.CONCURRENT_SESSIONS); - if (sessionControlElt != null) { new ConcurrentSessionsBeanDefinitionParser().parse(sessionControlElt, parserContext); } - - Element anonymousElt = DomUtils.getChildElementByTagName(element, Elements.ANONYMOUS); - if (anonymousElt != null) { + boolean autoConfig = false; + if ("true".equals(element.getAttribute(ATT_AUTO_CONFIG))) { + autoConfig = true; + } + + Element anonymousElt = DomUtils.getChildElementByTagName(element, Elements.ANONYMOUS); + if (anonymousElt != null || autoConfig) { new AnonymousBeanDefinitionParser().parse(anonymousElt, parserContext); } // Parse remember me before logout as RememberMeServices is also a LogoutHandler implementation. - - Element rememberMeElt = DomUtils.getChildElementByTagName(element, Elements.REMEMBER_ME); - - if (rememberMeElt != null) { + if (rememberMeElt != null || autoConfig) { new RememberMeBeanDefinitionParser().parse(rememberMeElt, parserContext); } Element logoutElt = DomUtils.getChildElementByTagName(element, Elements.LOGOUT); - - if (logoutElt != null) { + if (logoutElt != null || autoConfig) { new LogoutBeanDefinitionParser().parse(logoutElt, parserContext); } Element formLoginElt = DomUtils.getChildElementByTagName(element, Elements.FORM_LOGIN); - - if (formLoginElt != null) { + if (formLoginElt != null || autoConfig) { new FormLoginBeanDefinitionParser().parse(formLoginElt, parserContext); } - String realm = element.getAttribute(ATT_REALM); - if (!StringUtils.hasText(realm)) { - realm = DEF_REALM; - } - Element basicAuthElt = DomUtils.getChildElementByTagName(element, Elements.BASIC_AUTH); - - if (basicAuthElt != null) { + if (basicAuthElt != null || autoConfig) { new BasicAuthenticationBeanDefinitionParser(realm).parse(basicAuthElt, parserContext); } registry.registerBeanDefinition(BeanIds.FILTER_CHAIN_PROXY, filterChainProxy); - registry.registerBeanDefinition(BeanIds.HTTP_SESSION_CONTEXT_INTEGRATION_FILTER, httpSCIF); - registry.registerBeanDefinition(BeanIds.EXCEPTION_TRANSLATION_FILTER, - exceptionTranslationFilterBuilder.getBeanDefinition()); - registry.registerBeanDefinition(BeanIds.FILTER_SECURITY_INTERCEPTOR, - filterSecurityInterceptorBuilder.getBeanDefinition()); - + registry.registerBeanDefinition(BeanIds.HTTP_SESSION_CONTEXT_INTEGRATION_FILTER, httpScif); + registry.registerBeanDefinition(BeanIds.EXCEPTION_TRANSLATION_FILTER, exceptionTranslationFilterBuilder.getBeanDefinition()); + registry.registerBeanDefinition(BeanIds.FILTER_SECURITY_INTERCEPTOR, filterSecurityInterceptorBuilder.getBeanDefinition()); - // 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. - registry.registerBeanDefinition("__httpConfigBeanFactoryPostProcessor", - new RootBeanDefinition(HttpSecurityConfigPostProcessor.class)); + // 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. + registry.registerBeanDefinition("__httpConfigBeanFactoryPostProcessor", new RootBeanDefinition(HttpSecurityConfigPostProcessor.class)); return null; } @@ -228,7 +249,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser { String filters = urlElt.getAttribute(ATT_FILTERS); if (StringUtils.hasText(filters)) { - if (!filters.equals(NO_FILTERS_VALUE)) { + if (!filters.equals(OPT_FILTERS_NONE)) { parserContext.getReaderContext().error("Currently only 'none' is supported as the custom " + "filters attribute", urlElt); } diff --git a/core/src/main/java/org/springframework/security/config/LogoutBeanDefinitionParser.java b/core/src/main/java/org/springframework/security/config/LogoutBeanDefinitionParser.java index 074dc04373..9097532746 100644 --- a/core/src/main/java/org/springframework/security/config/LogoutBeanDefinitionParser.java +++ b/core/src/main/java/org/springframework/security/config/LogoutBeanDefinitionParser.java @@ -1,16 +1,14 @@ package org.springframework.security.config; -import org.springframework.security.ui.logout.LogoutFilter; -import org.springframework.security.ui.logout.SecurityContextLogoutHandler; -import org.springframework.beans.factory.BeanDefinitionStoreException; +import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.RuntimeBeanReference; -import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.ManagedList; -import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; +import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.security.ui.logout.LogoutFilter; +import org.springframework.security.ui.logout.SecurityContextLogoutHandler; import org.springframework.util.StringUtils; - import org.w3c.dom.Element; /** @@ -18,47 +16,60 @@ import org.w3c.dom.Element; * @author Ben Alex * @version $Id$ */ -public class LogoutBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { +public class LogoutBeanDefinitionParser implements BeanDefinitionParser { static final String ATT_LOGOUT_SUCCESS_URL = "logoutSuccessUrl"; - static final String ATT_LOGOUT_URL = "logoutUrl"; - public static final String DEF_LOGOUT_SUCCESS_URL = "/"; + static final String DEF_LOGOUT_SUCCESS_URL = "/"; - protected Class getBeanClass(Element element) { - return LogoutFilter.class; - } + static final String ATT_INVALIDATE_SESSION = "invalidateSession"; + static final String DEF_INVALIDATE_SESSION = "true"; + + static final String ATT_LOGOUT_URL = "logoutUrl"; + static final String DEF_LOGOUT_URL = "/j_spring_security_logout"; - protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { - String logoutUrl = element.getAttribute(ATT_LOGOUT_URL); + public BeanDefinition parse(Element element, ParserContext parserContext) { + String logoutUrl = null; + String logoutSuccessUrl = null; + String invalidateSession = null; - if (StringUtils.hasText(logoutUrl)) { - builder.addPropertyValue("filterProcessesUrl", logoutUrl); + if (element != null) { + logoutUrl = element.getAttribute(ATT_LOGOUT_URL); + logoutSuccessUrl = element.getAttribute(ATT_LOGOUT_SUCCESS_URL); + invalidateSession = element.getAttribute(ATT_INVALIDATE_SESSION); } - String logoutSuccessUrl = element.getAttribute(ATT_LOGOUT_SUCCESS_URL); + BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(LogoutFilter.class); + + if (!StringUtils.hasText(logoutUrl)) { + logoutUrl = DEF_LOGOUT_URL; + } + builder.addPropertyValue("filterProcessesUrl", logoutUrl); if (!StringUtils.hasText(logoutSuccessUrl)) { logoutSuccessUrl = DEF_LOGOUT_SUCCESS_URL; } - builder.addConstructorArg(logoutSuccessUrl); + + if (!StringUtils.hasText(invalidateSession)) { + invalidateSession = DEF_INVALIDATE_SESSION; + } + ManagedList handlers = new ManagedList(); - handlers.add(new SecurityContextLogoutHandler()); + SecurityContextLogoutHandler sclh = new SecurityContextLogoutHandler(); + if ("true".equals(invalidateSession)) { + sclh.setInvalidateHttpSession(true); + } else { + sclh.setInvalidateHttpSession(false); + } + handlers.add(sclh); if (parserContext.getRegistry().containsBeanDefinition(BeanIds.REMEMBER_ME_SERVICES)) { handlers.add(new RuntimeBeanReference(BeanIds.REMEMBER_ME_SERVICES)); } builder.addConstructorArg(handlers); + + parserContext.getRegistry().registerBeanDefinition(BeanIds.LOGOUT_FILTER, builder.getBeanDefinition()); - } - - protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) throws BeanDefinitionStoreException { - String id = super.resolveId(element, definition, parserContext); - - if (StringUtils.hasText(id)) { - return id; - } - - return BeanIds.LOGOUT_FILTER; - } + return null; + } } diff --git a/core/src/main/java/org/springframework/security/config/RememberMeBeanDefinitionParser.java b/core/src/main/java/org/springframework/security/config/RememberMeBeanDefinitionParser.java index 714ccadda2..1238eabfec 100644 --- a/core/src/main/java/org/springframework/security/config/RememberMeBeanDefinitionParser.java +++ b/core/src/main/java/org/springframework/security/config/RememberMeBeanDefinitionParser.java @@ -23,21 +23,29 @@ import org.w3c.dom.Element; */ public class RememberMeBeanDefinitionParser implements BeanDefinitionParser { static final String ATT_KEY = "key"; + static final String DEF_KEY = "doesNotMatter"; + static final String ATT_DATA_SOURCE = "dataSource"; static final String ATT_TOKEN_REPOSITORY = "tokenRepository"; protected final Log logger = LogFactory.getLog(getClass()); public BeanDefinition parse(Element element, ParserContext parserContext) { + String tokenRepository = null; + String dataSource = null; + String key = null; + + if (element != null) { + tokenRepository = element.getAttribute(ATT_TOKEN_REPOSITORY); + dataSource = element.getAttribute(ATT_DATA_SOURCE); + key = element.getAttribute(ATT_KEY); + } + BeanDefinition filter = new RootBeanDefinition(RememberMeProcessingFilter.class); BeanDefinition services = new RootBeanDefinition(PersistentTokenBasedRememberMeServices.class); filter.getPropertyValues().addPropertyValue("authenticationManager", new RuntimeBeanReference(BeanIds.AUTHENTICATION_MANAGER)); - String tokenRepository = element.getAttribute(ATT_TOKEN_REPOSITORY); - String dataSource = element.getAttribute(ATT_DATA_SOURCE); - String key = element.getAttribute(ATT_KEY); - boolean dataSourceSet = StringUtils.hasText(dataSource); boolean tokenRepoSet = StringUtils.hasText(tokenRepository); @@ -63,16 +71,15 @@ public class RememberMeBeanDefinitionParser implements BeanDefinitionParser { services = new RootBeanDefinition(TokenBasedRememberMeServices.class); } - if (StringUtils.hasText(key) && isPersistent) { - logger.warn("The attribute 'key' ('" + key + "') is not required for persistent remember-me services and " + - "will be ignored."); + if (!StringUtils.hasText(key) && !isPersistent) { + key = DEF_KEY; } - services.getPropertyValues().addPropertyValue(ATT_KEY, key); - BeanDefinition authManager = ConfigUtils.registerProviderManagerIfNecessary(parserContext); BeanDefinition provider = new RootBeanDefinition(RememberMeAuthenticationProvider.class); + provider.getPropertyValues().addPropertyValue(ATT_KEY, key); + services.getPropertyValues().addPropertyValue(ATT_KEY, key); ManagedList providers = (ManagedList) authManager.getPropertyValues().getPropertyValue("providers").getValue(); providers.add(provider); diff --git a/core/src/main/resources/org/springframework/security/config/spring-security-2.0.rnc b/core/src/main/resources/org/springframework/security/config/spring-security-2.0.rnc index c8189503e3..6372407c3e 100644 --- a/core/src/main/resources/org/springframework/security/config/spring-security-2.0.rnc +++ b/core/src/main/resources/org/springframework/security/config/spring-security-2.0.rnc @@ -10,9 +10,9 @@ start = http | ldap | repository # targetNamespace="http://www.springframework.org/schema/security" -path-type = - ## Defines the type types of pattern used to specify URL paths. Defaults to "ant" - [ a:defaultValue = "ant" ] attribute pathType {"regex" | "ant"} +pathType = + ## Defines the type of pattern used to specify URL paths (either JDK 1.4-compatible regular expressions, or Apache Ant expressions). Defaults to "ant" if unspecified. + attribute pathType {"ant" | "regex"} port = ## Specifies an IP port number. Used to configure an embedded LDAP server, for example. @@ -22,11 +22,6 @@ url = ## Specifies a URL. attribute url { xsd:string } -autoconfig = - ## Provides automatic security configration for a application - element autoconfig {autoconfig.attlist, empty} -autoconfig.attlist = empty - ldap = ## Sets up an ldap authentication provider, optionally with an embedded ldap server element ldap {ldap.attlist, empty} @@ -60,14 +55,17 @@ http = ## Container element for HTTP security configuration element http {http.attlist, (intercept-url+ & form-login? & http-basic? & logout? & concurrent-session-control? & remember-me? & anonymous?) } http.attlist &= - ## Controls the eagerness with which an HTTP session is created. - [ a:defaultValue = "ifRequired" ] attribute createSession {"ifRequired" | "always" | "never" }? + ## Automatically registers a login form, BASIC authentication, anonymous authentication, logout services and remember-me. If set to "true", all of these capabilities are added (although you can still customize the configuration of each by providing the respective element). If unspecified, defaults to "false". + attribute autoConfig {"true" | "false" }? +http.attlist &= + ## Controls the eagerness with which an HTTP session is created. If not set, defaults to "ifRequired". + attribute createSession {"ifRequired" | "always" | "never" }? http.attlist &= - ## The path format used to define the paths used in child elements. - path-type? + ## The path format used to define the paths in child elements. + pathType? http.attlist &= - ## Whether test URLs should be converted to lower case prior to comparing with defined path patterns. - [ a:defaultValue = "true" ] attribute lowerCaseComparisons {"true" | "false"}? + ## Whether test URLs should be converted to lower case prior to comparing with defined path patterns. If unspecified, defaults to "true". + attribute lowercaseComparisons {"true" | "false"}? http.attlist &= ## Optional attribute specifying the ID of the AccessDecisionManager implementation which should be used for authorizing HTTP requests. attribute accessDecisionManager {xsd:string}? @@ -93,13 +91,17 @@ intercept-url.attlist &= attribute requiresChannel {"http" | "https"}? logout = + ## Incorporates a logout processing filter. Most web applications require a logout filter, although you may not require one if you write a controller to provider similar logic. element logout {logout.attlist, empty} logout.attlist &= - [ a:defaultValue = "/j_spring_security_logout" ] attribute logoutUrl {xsd:string}? + ## Specifies the URL that will cause a logout. Spring Security will initialize a filter that responds to this particular URL. Defaults to /j_spring_security_logout if unspecified. + attribute logoutUrl {xsd:string}? logout.attlist &= - [ a:defaultValue = "/" ] attribute logoutSuccessUrl {xsd:string}? + ## Specifies the URL to display once the user has logged out. If not specified, defaults to /. + attribute logoutSuccessUrl {xsd:string}? logout.attlist &= - [ a:defaultValue = "true" ] attribute invalidateSession {"true" | "false"}? + ## Specifies whether a logout also causes HttpSession invalidation, which is generally desirable. If unspecified, defaults to true. + attribute invalidateSession {"true" | "false"}? form-login = ## Sets up a form login configuration @@ -121,7 +123,7 @@ filter-chain-map = ## Used to explicitly configure a FilterChainProxy instance with a FilterChainMap element filter-chain-map {filter-chain-map.attlist, filter-chain+} filter-chain-map.attlist &= - path-type + pathType filter-chain = ## Used within filter-chain-map to define a specific URL pattern and the list of filters which apply to the URLs matching that pattern. When multiple filter-chain elements are used within a filter-chain-map element, the most specific patterns must be placed at the top of the list, with most general ones at the bottom. @@ -155,14 +157,14 @@ anonymous = ## Adds support for automatically granting all anonymous web requests a particular principal identity and a corresponding granted authority. element anonymous {anonymous.attlist} anonymous.attlist &= - ## The key used between the provider and filter. This generally does not need to be set. - [ a:defaultValue = "doesNotMatter" ] attribute key {xsd:string}? + ## The key used between the provider and filter. This generally does not need to be set. If unset, it will default to "doesNotMatter". + attribute key {xsd:string}? anonymous.attlist &= - ## The username that should be assigned to the anonymous request. This allows the principal to be identified, which may be important for logging and auditing. - [ a:defaultValue = "anonymousUser" ] attribute username {xsd:string}? + ## The username that should be assigned to the anonymous request. This allows the principal to be identified, which may be important for logging and auditing. if unset, defaults to "anonymousUser". + attribute username {xsd:string}? anonymous.attlist &= - ## The granted authority that should be assigned to the anonymous request. Commonly this is used to assign the anonymous request particular roles, which can subsequently be used in authorization decisions. - [ a:defaultValue = "ROLE_ANONYMOUS" ] attribute grantedAuthority {xsd:string}? + ## The granted authority that should be assigned to the anonymous request. Commonly this is used to assign the anonymous request particular roles, which can subsequently be used in authorization decisions. If unset, defaults to "ROLE_ANONYMOUS". + attribute grantedAuthority {xsd:string}? repository = element repository {repository.attlist, (user-service | jdbc-user-service | custom-user-service)} diff --git a/core/src/main/resources/org/springframework/security/config/spring-security-2.0.xsd b/core/src/main/resources/org/springframework/security/config/spring-security-2.0.xsd index 94abcf4126..17715cedc8 100644 --- a/core/src/main/resources/org/springframework/security/config/spring-security-2.0.xsd +++ b/core/src/main/resources/org/springframework/security/config/spring-security-2.0.xsd @@ -1,15 +1,15 @@ - + - Defines the type types of pattern used to specify URL paths. Defaults to "ant" + Defines the type of pattern used to specify URL paths (either JDK 1.4-compatible regular expressions, or Apache Ant expressions). Defaults to "ant" if unspecified. - + @@ -28,12 +28,6 @@ - - - Provides automatic security configration for a application - - - Sets up an ldap authentication provider, optionally with an embedded ldap server @@ -107,9 +101,20 @@ - + - Controls the eagerness with which an HTTP session is created. + Automatically registers a login form, BASIC authentication, anonymous authentication, logout services and remember-me. If set to "true", all of these capabilities are added (although you can still customize the configuration of each by providing the respective element). If unspecified, defaults to "false". + + + + + + + + + + + Controls the eagerness with which an HTTP session is created. If not set, defaults to "ifRequired". @@ -121,18 +126,18 @@ - Defines the type types of pattern used to specify URL paths. Defaults to "ant" + Defines the type of pattern used to specify URL paths (either JDK 1.4-compatible regular expressions, or Apache Ant expressions). Defaults to "ant" if unspecified. - + - + - Whether test URLs should be converted to lower case prior to comparing with defined path patterns. + Whether test URLs should be converted to lower case prior to comparing with defined path patterns. If unspecified, defaults to "true". @@ -194,14 +199,28 @@ + + Incorporates a logout processing filter. Most web applications require a logout filter, although you may not require one if you write a controller to provider similar logic. + - - - + + + Specifies the URL that will cause a logout. Spring Security will initialize a filter that responds to this particular URL. Defaults to /j_spring_security_logout if unspecified. + + + + + Specifies the URL to display once the user has logged out. If not specified, defaults to /. + + + + + Specifies whether a logout also causes HttpSession invalidation, which is generally desirable. If unspecified, defaults to true. + @@ -252,7 +271,7 @@ - + @@ -311,19 +330,19 @@ - + - The key used between the provider and filter. This generally does not need to be set. + The key used between the provider and filter. This generally does not need to be set. If unset, it will default to "doesNotMatter". - + - The username that should be assigned to the anonymous request. This allows the principal to be identified, which may be important for logging and auditing. + The username that should be assigned to the anonymous request. This allows the principal to be identified, which may be important for logging and auditing. if unset, defaults to "anonymousUser". - + - The granted authority that should be assigned to the anonymous request. Commonly this is used to assign the anonymous request particular roles, which can subsequently be used in authorization decisions. + The granted authority that should be assigned to the anonymous request. Commonly this is used to assign the anonymous request particular roles, which can subsequently be used in authorization decisions. If unset, defaults to "ROLE_ANONYMOUS". diff --git a/core/src/test/resources/org/springframework/security/config/http-security.xml b/core/src/test/resources/org/springframework/security/config/http-security.xml index 1101957f43..ef25eb89c0 100644 --- a/core/src/test/resources/org/springframework/security/config/http-security.xml +++ b/core/src/test/resources/org/springframework/security/config/http-security.xml @@ -6,19 +6,19 @@ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.xsd"> - + - + - + diff --git a/samples/tutorial/src/main/webapp/WEB-INF/applicationContext-security-ns.xml b/samples/tutorial/src/main/webapp/WEB-INF/applicationContext-security-ns.xml index bd351fb7ee..e62775ca74 100644 --- a/samples/tutorial/src/main/webapp/WEB-INF/applicationContext-security-ns.xml +++ b/samples/tutorial/src/main/webapp/WEB-INF/applicationContext-security-ns.xml @@ -11,23 +11,27 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.xsd"> - - + + - + + - + + + - - - + --> +