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 317be2f9a3..64547b3834 100644 --- a/core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java +++ b/core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java @@ -50,16 +50,16 @@ import org.w3c.dom.Element; * @version $Id$ */ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser { - static final Log logger = LogFactory.getLog(HttpSecurityBeanDefinitionParser.class); + static final Log logger = LogFactory.getLog(HttpSecurityBeanDefinitionParser.class); static final String ATT_REALM = "realm"; static final String DEF_REALM = "Spring Security Application"; static final String ATT_PATH_PATTERN = "pattern"; - + static final String ATT_SESSION_FIXATION_PROTECTION = "session-fixation-protection"; static final String OPT_SESSION_FIXATION_NO_PROTECTION = "none"; - static final String OPT_SESSION_FIXATION_CLEAN_SESSION = "newSession"; + static final String OPT_SESSION_FIXATION_CLEAN_SESSION = "newSession"; static final String OPT_SESSION_FIXATION_MIGRATE_SESSION = "migrateSession"; static final String ATT_PATH_TYPE = "path-type"; @@ -91,9 +91,9 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser { static final String ATT_SERVLET_API_PROVISION = "servlet-api-provision"; static final String DEF_SERVLET_API_PROVISION = "true"; - static final String ATT_ACCESS_MGR = "access-decision-manager-ref"; + static final String ATT_ACCESS_MGR = "access-decision-manager-ref"; static final String ATT_USER_SERVICE_REF = "user-service-ref"; - + static final String ATT_ENTRY_POINT_REF = "entry-point-ref"; static final String ATT_ONCE_PER_REQUEST = "once-per-request"; static final String ATT_ACCESS_DENIED_PAGE = "access-denied-page"; @@ -106,20 +106,20 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser { // SEC-501 - should paths stored in request maps be converted to lower case // true if Ant path and using lower case final boolean convertPathsToLowerCase = (matcher instanceof AntUrlPathMatcher) && matcher.requiresLowerCaseUrl(); - + final List interceptUrlElts = DomUtils.getChildElementsByTagName(element, Elements.INTERCEPT_URL); final Map filterChainMap = new LinkedHashMap(); final LinkedHashMap channelRequestMap = new LinkedHashMap(); registerFilterChainProxy(parserContext, filterChainMap, matcher, source); - - parseInterceptUrlsForChannelSecurityAndFilterChain(interceptUrlElts, filterChainMap, channelRequestMap, + + parseInterceptUrlsForChannelSecurityAndFilterChain(interceptUrlElts, filterChainMap, channelRequestMap, convertPathsToLowerCase, parserContext); boolean allowSessionCreation = registerHttpSessionIntegrationFilter(element, parserContext); - + registerServletApiFilter(element, parserContext); - + // Set up the access manager reference for http String accessManagerId = element.getAttribute(ATT_ACCESS_MGR); @@ -127,7 +127,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser { ConfigUtils.registerDefaultAccessManagerIfNecessary(parserContext); accessManagerId = BeanIds.ACCESS_MANAGER; } - + // Register the portMapper. A default will always be created, even if no element exists. BeanDefinition portMapper = new PortMappingsBeanDefinitionParser().parse( DomUtils.getChildElementByTagName(element, Elements.PORT_MAPPINGS), parserContext); @@ -139,19 +139,19 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser { if (channelRequestMap.size() > 0) { // At least one channel requirement has been specified registerChannelProcessingBeans(parserContext, matcher, channelRequestMap); - } - - registerFilterSecurityInterceptor(element, parserContext, matcher, accessManagerId, + } + + registerFilterSecurityInterceptor(element, parserContext, matcher, accessManagerId, parseInterceptUrlsForFilterInvocationRequestMap(interceptUrlElts, convertPathsToLowerCase, parserContext)); boolean sessionControlEnabled = registerConcurrentSessionControlBeansIfRequired(element, parserContext); - - registerSessionFixationProtectionFilter(parserContext, element.getAttribute(ATT_SESSION_FIXATION_PROTECTION), + + registerSessionFixationProtectionFilter(parserContext, element.getAttribute(ATT_SESSION_FIXATION_PROTECTION), sessionControlEnabled); boolean autoConfig = false; if ("true".equals(element.getAttribute(ATT_AUTO_CONFIG))) { - autoConfig = true; + autoConfig = true; } Element anonymousElt = DomUtils.getChildElementByTagName(element, Elements.ANONYMOUS); @@ -160,7 +160,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser { } parseRememberMeAndLogout(element, autoConfig, parserContext); - + parseBasicFormLoginAndOpenID(element, parserContext, autoConfig, allowSessionCreation); Element x509Elt = DomUtils.getChildElementByTagName(element, Elements.X509); @@ -175,48 +175,49 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser { RootBeanDefinition postProcessor2 = new RootBeanDefinition(UserDetailsServiceInjectionBeanPostProcessor.class); postProcessor2.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); registry.registerBeanDefinition(BeanIds.USER_DETAILS_SERVICE_INJECTION_POST_PROCESSOR, postProcessor2); - + return null; } - + private void parseRememberMeAndLogout(Element elt, boolean autoConfig, ParserContext pc) { // Parse remember me before logout as RememberMeServices is also a LogoutHandler implementation. Element rememberMeElt = DomUtils.getChildElementByTagName(elt, Elements.REMEMBER_ME); String rememberMeServices = null; - + if (rememberMeElt != null || autoConfig) { - RememberMeBeanDefinitionParser rmbdp = new RememberMeBeanDefinitionParser(); - rmbdp.parse(rememberMeElt, pc); - rememberMeServices = rmbdp.getServicesName(); + RememberMeBeanDefinitionParser rmbdp = new RememberMeBeanDefinitionParser(); + rmbdp.parse(rememberMeElt, pc); + rememberMeServices = rmbdp.getServicesName(); // Post processor to inject RememberMeServices into filters which need it RootBeanDefinition rememberMeInjectionPostProcessor = new RootBeanDefinition(RememberMeServicesInjectionBeanPostProcessor.class); rememberMeInjectionPostProcessor.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); - pc.getRegistry().registerBeanDefinition(BeanIds.REMEMBER_ME_SERVICES_INJECTION_POST_PROCESSOR, rememberMeInjectionPostProcessor); + pc.getRegistry().registerBeanDefinition(BeanIds.REMEMBER_ME_SERVICES_INJECTION_POST_PROCESSOR, rememberMeInjectionPostProcessor); } - + Element logoutElt = DomUtils.getChildElementByTagName(elt, Elements.LOGOUT); if (logoutElt != null || autoConfig) { new LogoutBeanDefinitionParser(rememberMeServices).parse(logoutElt, pc); - } + } } - + private void registerFilterChainProxy(ParserContext pc, Map filterChainMap, UrlMatcher matcher, Object source) { if (pc.getRegistry().containsBeanDefinition(BeanIds.FILTER_CHAIN_PROXY)) { pc.getReaderContext().error("Duplicate element detected", source); } - + RootBeanDefinition filterChainProxy = new RootBeanDefinition(FilterChainProxy.class); filterChainProxy.setSource(source); filterChainProxy.getPropertyValues().addPropertyValue("matcher", matcher); + filterChainProxy.getPropertyValues().addPropertyValue("stripQueryStringFromUrls", matcher instanceof AntUrlPathMatcher); filterChainProxy.getPropertyValues().addPropertyValue("filterChainMap", filterChainMap); pc.getRegistry().registerBeanDefinition(BeanIds.FILTER_CHAIN_PROXY, filterChainProxy); - pc.getRegistry().registerAlias(BeanIds.FILTER_CHAIN_PROXY, BeanIds.SPRING_SECURITY_FILTER_CHAIN); + pc.getRegistry().registerAlias(BeanIds.FILTER_CHAIN_PROXY, BeanIds.SPRING_SECURITY_FILTER_CHAIN); } private boolean registerHttpSessionIntegrationFilter(Element element, ParserContext pc) { RootBeanDefinition httpScif = new RootBeanDefinition(HttpSessionContextIntegrationFilter.class); boolean sessionCreationAllowed = true; - + String createSession = element.getAttribute(ATT_CREATE_SESSION); if (OPT_CREATE_SESSION_ALWAYS.equals(createSession)) { httpScif.getPropertyValues().addPropertyValue("allowSessionCreation", Boolean.TRUE); @@ -233,11 +234,11 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser { pc.getRegistry().registerBeanDefinition(BeanIds.HTTP_SESSION_CONTEXT_INTEGRATION_FILTER, httpScif); ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.HTTP_SESSION_CONTEXT_INTEGRATION_FILTER)); - + return sessionCreationAllowed; } - // Adds the servlet-api integration filter if required + // Adds the servlet-api integration filter if required private void registerServletApiFilter(Element element, ParserContext pc) { String provideServletApi = element.getAttribute(ATT_SERVLET_API_PROVISION); if (!StringUtils.hasText(provideServletApi)) { @@ -250,27 +251,27 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser { ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.SECURITY_CONTEXT_HOLDER_AWARE_REQUEST_FILTER)); } } - + private boolean registerConcurrentSessionControlBeansIfRequired(Element element, ParserContext parserContext) { Element sessionControlElt = DomUtils.getChildElementByTagName(element, Elements.CONCURRENT_SESSIONS); if (sessionControlElt == null) { return false; } - + new ConcurrentSessionsBeanDefinitionParser().parse(sessionControlElt, parserContext); logger.info("Concurrent session filter in use, setting 'forceEagerSessionCreation' to true"); - BeanDefinition sessionIntegrationFilter = parserContext.getRegistry().getBeanDefinition(BeanIds.HTTP_SESSION_CONTEXT_INTEGRATION_FILTER); + BeanDefinition sessionIntegrationFilter = parserContext.getRegistry().getBeanDefinition(BeanIds.HTTP_SESSION_CONTEXT_INTEGRATION_FILTER); sessionIntegrationFilter.getPropertyValues().addPropertyValue("forceEagerSessionCreation", Boolean.TRUE); return true; } - + private void registerExceptionTranslationFilter(Element element, ParserContext pc, boolean allowSessionCreation) { - String accessDeniedPage = element.getAttribute(ATT_ACCESS_DENIED_PAGE); - ConfigUtils.validateHttpRedirect(accessDeniedPage, pc, pc.extractSource(element)); + String accessDeniedPage = element.getAttribute(ATT_ACCESS_DENIED_PAGE); + ConfigUtils.validateHttpRedirect(accessDeniedPage, pc, pc.extractSource(element)); BeanDefinitionBuilder exceptionTranslationFilterBuilder = BeanDefinitionBuilder.rootBeanDefinition(ExceptionTranslationFilter.class); exceptionTranslationFilterBuilder.addPropertyValue("createSessionAllowed", new Boolean(allowSessionCreation)); - + if (StringUtils.hasText(accessDeniedPage)) { AccessDeniedHandlerImpl accessDeniedHandler = new AccessDeniedHandlerImpl(); accessDeniedHandler.setErrorPage(accessDeniedPage); @@ -280,27 +281,27 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser { pc.getRegistry().registerBeanDefinition(BeanIds.EXCEPTION_TRANSLATION_FILTER, exceptionTranslationFilterBuilder.getBeanDefinition()); ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.EXCEPTION_TRANSLATION_FILTER)); } - + private void registerFilterSecurityInterceptor(Element element, ParserContext pc, UrlMatcher matcher, String accessManagerId, LinkedHashMap filterInvocationDefinitionMap) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(FilterSecurityInterceptor.class); builder.addPropertyReference("accessDecisionManager", accessManagerId); builder.addPropertyReference("authenticationManager", BeanIds.AUTHENTICATION_MANAGER); - + if ("false".equals(element.getAttribute(ATT_ONCE_PER_REQUEST))) { builder.addPropertyValue("observeOncePerRequest", Boolean.FALSE); } - - DefaultFilterInvocationDefinitionSource fids = + + DefaultFilterInvocationDefinitionSource fids = new DefaultFilterInvocationDefinitionSource(matcher, filterInvocationDefinitionMap); fids.setStripQueryStringFromUrls(matcher instanceof AntUrlPathMatcher); - + builder.addPropertyValue("objectDefinitionSource", fids); pc.getRegistry().registerBeanDefinition(BeanIds.FILTER_SECURITY_INTERCEPTOR, builder.getBeanDefinition()); ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.FILTER_SECURITY_INTERCEPTOR)); } - + private void registerChannelProcessingBeans(ParserContext pc, UrlMatcher matcher, LinkedHashMap channelRequestMap) { RootBeanDefinition channelFilter = new RootBeanDefinition(ChannelProcessingFilter.class); channelFilter.getPropertyValues().addPropertyValue("channelDecisionManager", @@ -308,7 +309,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser { DefaultFilterInvocationDefinitionSource channelFilterInvDefSource = new DefaultFilterInvocationDefinitionSource(matcher, channelRequestMap); channelFilterInvDefSource.setStripQueryStringFromUrls(matcher instanceof AntUrlPathMatcher); - + channelFilter.getPropertyValues().addPropertyValue("filterInvocationDefinitionSource", channelFilterInvDefSource); RootBeanDefinition channelDecisionManager = new RootBeanDefinition(ChannelDecisionManagerImpl.class); @@ -329,161 +330,161 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser { pc.getRegistry().registerBeanDefinition(BeanIds.CHANNEL_PROCESSING_FILTER, channelFilter); ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.CHANNEL_PROCESSING_FILTER)); pc.getRegistry().registerBeanDefinition(BeanIds.CHANNEL_DECISION_MANAGER, channelDecisionManager); - + } - + private void registerSessionFixationProtectionFilter(ParserContext pc, String sessionFixationAttribute, boolean sessionControlEnabled) { if(!StringUtils.hasText(sessionFixationAttribute)) { sessionFixationAttribute = OPT_SESSION_FIXATION_MIGRATE_SESSION; } - + if (!sessionFixationAttribute.equals(OPT_SESSION_FIXATION_NO_PROTECTION)) { - BeanDefinitionBuilder sessionFixationFilter = + BeanDefinitionBuilder sessionFixationFilter = BeanDefinitionBuilder.rootBeanDefinition(SessionFixationProtectionFilter.class); - sessionFixationFilter.addPropertyValue("migrateSessionAttributes", + sessionFixationFilter.addPropertyValue("migrateSessionAttributes", Boolean.valueOf(sessionFixationAttribute.equals(OPT_SESSION_FIXATION_MIGRATE_SESSION))); if (sessionControlEnabled) { sessionFixationFilter.addPropertyReference("sessionRegistry", BeanIds.SESSION_REGISTRY); } - pc.getRegistry().registerBeanDefinition(BeanIds.SESSION_FIXATION_PROTECTION_FILTER, + pc.getRegistry().registerBeanDefinition(BeanIds.SESSION_FIXATION_PROTECTION_FILTER, sessionFixationFilter.getBeanDefinition()); ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.SESSION_FIXATION_PROTECTION_FILTER)); } } - + private void parseBasicFormLoginAndOpenID(Element element, ParserContext pc, boolean autoConfig, boolean allowSessionCreation) { RootBeanDefinition formLoginFilter = null; RootBeanDefinition formLoginEntryPoint = null; - String formLoginPage = null; + String formLoginPage = null; RootBeanDefinition openIDFilter = null; RootBeanDefinition openIDEntryPoint = null; String openIDLoginPage = null; - + String realm = element.getAttribute(ATT_REALM); if (!StringUtils.hasText(realm)) { - realm = DEF_REALM; + realm = DEF_REALM; } - + Element basicAuthElt = DomUtils.getChildElementByTagName(element, Elements.BASIC_AUTH); if (basicAuthElt != null || autoConfig) { new BasicAuthenticationBeanDefinitionParser(realm).parse(basicAuthElt, pc); } - - Element formLoginElt = DomUtils.getChildElementByTagName(element, Elements.FORM_LOGIN); - + + Element formLoginElt = DomUtils.getChildElementByTagName(element, Elements.FORM_LOGIN); + if (formLoginElt != null || autoConfig) { - FormLoginBeanDefinitionParser parser = new FormLoginBeanDefinitionParser("/j_spring_security_check", - "org.springframework.security.ui.webapp.AuthenticationProcessingFilter"); - + FormLoginBeanDefinitionParser parser = new FormLoginBeanDefinitionParser("/j_spring_security_check", + "org.springframework.security.ui.webapp.AuthenticationProcessingFilter"); + parser.parse(formLoginElt, pc); formLoginFilter = parser.getFilterBean(); formLoginEntryPoint = parser.getEntryPointBean(); formLoginPage = parser.getLoginPage(); } - + Element openIDLoginElt = DomUtils.getChildElementByTagName(element, Elements.OPENID_LOGIN); if (openIDLoginElt != null) { - FormLoginBeanDefinitionParser parser = new FormLoginBeanDefinitionParser("/j_spring_openid_security_check", - "org.springframework.security.ui.openid.OpenIDAuthenticationProcessingFilter"); - + FormLoginBeanDefinitionParser parser = new FormLoginBeanDefinitionParser("/j_spring_openid_security_check", + "org.springframework.security.ui.openid.OpenIDAuthenticationProcessingFilter"); + parser.parse(openIDLoginElt, pc); openIDFilter = parser.getFilterBean(); openIDEntryPoint = parser.getEntryPointBean(); openIDLoginPage = parser.getLoginPage(); - - BeanDefinitionBuilder openIDProviderBuilder = + + BeanDefinitionBuilder openIDProviderBuilder = BeanDefinitionBuilder.rootBeanDefinition("org.springframework.security.providers.openid.OpenIDAuthenticationProvider"); - + String userService = openIDLoginElt.getAttribute(ATT_USER_SERVICE_REF); - + if (StringUtils.hasText(userService)) { openIDProviderBuilder.addPropertyReference("userDetailsService", userService); } - + BeanDefinition openIDProvider = openIDProviderBuilder.getBeanDefinition(); pc.getRegistry().registerBeanDefinition(BeanIds.OPEN_ID_PROVIDER, openIDProvider); ConfigUtils.addAuthenticationProvider(pc, BeanIds.OPEN_ID_PROVIDER); } - + boolean needLoginPage = false; - + if (formLoginFilter != null) { - needLoginPage = true; - formLoginFilter.getPropertyValues().addPropertyValue("allowSessionCreation", new Boolean(allowSessionCreation)); - pc.getRegistry().registerBeanDefinition(BeanIds.FORM_LOGIN_FILTER, formLoginFilter); - ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.FORM_LOGIN_FILTER)); - pc.getRegistry().registerBeanDefinition(BeanIds.FORM_LOGIN_ENTRY_POINT, formLoginEntryPoint); - } + needLoginPage = true; + formLoginFilter.getPropertyValues().addPropertyValue("allowSessionCreation", new Boolean(allowSessionCreation)); + pc.getRegistry().registerBeanDefinition(BeanIds.FORM_LOGIN_FILTER, formLoginFilter); + ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.FORM_LOGIN_FILTER)); + pc.getRegistry().registerBeanDefinition(BeanIds.FORM_LOGIN_ENTRY_POINT, formLoginEntryPoint); + } if (openIDFilter != null) { - needLoginPage = true; - openIDFilter.getPropertyValues().addPropertyValue("allowSessionCreation", new Boolean(allowSessionCreation)); - pc.getRegistry().registerBeanDefinition(BeanIds.OPEN_ID_FILTER, openIDFilter); - ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.OPEN_ID_FILTER)); - pc.getRegistry().registerBeanDefinition(BeanIds.OPEN_ID_ENTRY_POINT, openIDEntryPoint); + needLoginPage = true; + openIDFilter.getPropertyValues().addPropertyValue("allowSessionCreation", new Boolean(allowSessionCreation)); + pc.getRegistry().registerBeanDefinition(BeanIds.OPEN_ID_FILTER, openIDFilter); + ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.OPEN_ID_FILTER)); + pc.getRegistry().registerBeanDefinition(BeanIds.OPEN_ID_ENTRY_POINT, openIDEntryPoint); } // If no login page has been defined, add in the default page generator. 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 = - BeanDefinitionBuilder.rootBeanDefinition(DefaultLoginPageGeneratingFilter.class); - + BeanDefinitionBuilder loginPageFilter = + BeanDefinitionBuilder.rootBeanDefinition(DefaultLoginPageGeneratingFilter.class); + if (formLoginFilter != null) { - loginPageFilter.addConstructorArg(new RuntimeBeanReference(BeanIds.FORM_LOGIN_FILTER)); + loginPageFilter.addConstructorArg(new RuntimeBeanReference(BeanIds.FORM_LOGIN_FILTER)); } - + if (openIDFilter != null) { - loginPageFilter.addConstructorArg(new RuntimeBeanReference(BeanIds.OPEN_ID_FILTER)); + loginPageFilter.addConstructorArg(new RuntimeBeanReference(BeanIds.OPEN_ID_FILTER)); } - pc.getRegistry().registerBeanDefinition(BeanIds.DEFAULT_LOGIN_PAGE_GENERATING_FILTER, - loginPageFilter.getBeanDefinition()); + pc.getRegistry().registerBeanDefinition(BeanIds.DEFAULT_LOGIN_PAGE_GENERATING_FILTER, + loginPageFilter.getBeanDefinition()); ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.DEFAULT_LOGIN_PAGE_GENERATING_FILTER)); } - + // We need to establish the main entry point. // First check if a custom entry point bean is set String customEntryPoint = element.getAttribute(ATT_ENTRY_POINT_REF); - + if (StringUtils.hasText(customEntryPoint)) { pc.getRegistry().registerAlias(customEntryPoint, BeanIds.MAIN_ENTRY_POINT); return; } - + // Basic takes precedence if explicit element is used and no others are configured if (basicAuthElt != null && formLoginElt == null && openIDLoginElt == null) { - pc.getRegistry().registerAlias(BeanIds.BASIC_AUTHENTICATION_ENTRY_POINT, BeanIds.MAIN_ENTRY_POINT); - return; + pc.getRegistry().registerAlias(BeanIds.BASIC_AUTHENTICATION_ENTRY_POINT, BeanIds.MAIN_ENTRY_POINT); + return; } - + // If formLogin has been enabled either through an element or auto-config, then it is used if no openID login page // has been set if (formLoginFilter != null && openIDLoginPage == null) { - pc.getRegistry().registerAlias(BeanIds.FORM_LOGIN_ENTRY_POINT, BeanIds.MAIN_ENTRY_POINT); - return; + pc.getRegistry().registerAlias(BeanIds.FORM_LOGIN_ENTRY_POINT, BeanIds.MAIN_ENTRY_POINT); + return; } - + // Otherwise use OpenID if enabled if (openIDFilter != null && formLoginFilter == null) { - pc.getRegistry().registerAlias(BeanIds.OPEN_ID_ENTRY_POINT, BeanIds.MAIN_ENTRY_POINT); - return; + pc.getRegistry().registerAlias(BeanIds.OPEN_ID_ENTRY_POINT, BeanIds.MAIN_ENTRY_POINT); + return; } - + // If X.509 has been enabled, use the preauth entry point. if (DomUtils.getChildElementByTagName(element, Elements.X509) != null) { pc.getRegistry().registerAlias(BeanIds.PRE_AUTH_ENTRY_POINT, BeanIds.MAIN_ENTRY_POINT); return; } - + pc.getReaderContext().error("No AuthenticationEntryPoint could be established. Please " + - "make sure you have a login mechanism configured through the namespace (such as form-login) or " + - "specify a custom AuthenticationEntryPoint with the custom-entry-point-ref attribute ", + "make sure you have a login mechanism configured through the namespace (such as form-login) or " + + "specify a custom AuthenticationEntryPoint with the custom-entry-point-ref attribute ", pc.extractSource(element)); } - + static UrlMatcher createUrlMatcher(Element element) { String patternType = element.getAttribute(ATT_PATH_TYPE); if (!StringUtils.hasText(patternType)) { @@ -517,8 +518,8 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser { } // Default for regex is no change } - - return matcher; + + return matcher; } /** @@ -562,7 +563,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser { editor.setAsText(channelConfigAttribute); channelRequestMap.put(new RequestKey(path), (ConfigAttributeDefinition) editor.getValue()); } - + String filters = urlElt.getAttribute(ATT_FILTERS); if (StringUtils.hasText(filters)) { @@ -575,10 +576,10 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser { } } } - + static LinkedHashMap parseInterceptUrlsForFilterInvocationRequestMap(List urlElts, boolean useLowerCasePaths, ParserContext parserContext) { LinkedHashMap filterInvocationDefinitionMap = new LinkedHashMap(); - + Iterator urlEltsIterator = urlElts.iterator(); ConfigAttributeEditor editor = new ConfigAttributeEditor(); @@ -606,16 +607,16 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser { if (StringUtils.hasText(access)) { editor.setAsText(access); Object key = new RequestKey(path, method); - + if (filterInvocationDefinitionMap.containsKey(key)) { - logger.warn("Duplicate URL defined: " + key + ". The original attribute values will be overwritten"); + logger.warn("Duplicate URL defined: " + key + ". The original attribute values will be overwritten"); } - + filterInvocationDefinitionMap.put(key, editor.getValue()); } } - + return filterInvocationDefinitionMap; } - + } diff --git a/core/src/main/java/org/springframework/security/util/FilterChainProxy.java b/core/src/main/java/org/springframework/security/util/FilterChainProxy.java index 75435b61ad..f605cf9afa 100644 --- a/core/src/main/java/org/springframework/security/util/FilterChainProxy.java +++ b/core/src/main/java/org/springframework/security/util/FilterChainProxy.java @@ -107,6 +107,7 @@ public class FilterChainProxy implements Filter, InitializingBean, ApplicationCo /** Compiled pattern version of the filter chain map */ private Map filterChainMap; private UrlMatcher matcher = new AntUrlPathMatcher(); + private boolean stripQueryStringFromUrls = true; private DefaultFilterInvocationDefinitionSource fids; //~ Methods ======================================================================================================== @@ -116,8 +117,8 @@ public class FilterChainProxy implements Filter, InitializingBean, ApplicationCo if (fids != null) { Assert.isNull(uncompiledFilterChainMap, "Set the filterChainMap or FilterInvocationDefinitionSource but not both"); FIDSToFilterChainMapConverter converter = new FIDSToFilterChainMapConverter(fids, applicationContext); - setMatcher(converter.getMatcher()); - setFilterChainMap(converter.getFilterChainMap()); + setMatcher(converter.getMatcher()); + setFilterChainMap(converter.getFilterChainMap()); fids = null; } @@ -181,6 +182,16 @@ public class FilterChainProxy implements Filter, InitializingBean, ApplicationCo * @return an ordered array of Filters defining the filter chain */ public List getFilters(String url) { + if (stripQueryStringFromUrls) { + // String query string - see SEC-953 + int firstQuestionMarkIndex = url.indexOf("?"); + + if (firstQuestionMarkIndex != -1) { + url = url.substring(0, firstQuestionMarkIndex); + } + } + + Iterator filterChains = filterChainMap.entrySet().iterator(); while (filterChains.hasNext()) { @@ -319,6 +330,14 @@ public class FilterChainProxy implements Filter, InitializingBean, ApplicationCo return matcher; } + /** + * If set to 'true', the query string will be stripped from the request URL before + * attempting to find a matching filter chain. This is the default value. + */ + public void setStripQueryStringFromUrls(boolean stripQueryStringFromUrls) { + this.stripQueryStringFromUrls = stripQueryStringFromUrls; + } + public String toString() { StringBuffer sb = new StringBuffer(); sb.append("FilterChainProxy["); diff --git a/core/src/test/java/org/springframework/security/config/HttpSecurityBeanDefinitionParserTests.java b/core/src/test/java/org/springframework/security/config/HttpSecurityBeanDefinitionParserTests.java index a4f6e6348e..972c8e1cdf 100644 --- a/core/src/test/java/org/springframework/security/config/HttpSecurityBeanDefinitionParserTests.java +++ b/core/src/test/java/org/springframework/security/config/HttpSecurityBeanDefinitionParserTests.java @@ -79,6 +79,8 @@ public class HttpSecurityBeanDefinitionParserTests { List filterList = getFilters("/anyurl"); checkAutoConfigFilters(filterList); + + assertEquals(true, FieldUtils.getFieldValue(appContext.getBean("_filterChainProxy"), "stripQueryStringFromUrls")); assertEquals(true, FieldUtils.getFieldValue(filterList.get(10), "objectDefinitionSource.stripQueryStringFromUrls")); } @@ -136,6 +138,7 @@ public class HttpSecurityBeanDefinitionParserTests { // This will be matched by the default pattern ".*" List allFilters = getFilters("/ImCaughtByTheUniversalMatchPattern"); checkAutoConfigFilters(allFilters); + assertEquals(false, FieldUtils.getFieldValue(appContext.getBean("_filterChainProxy"), "stripQueryStringFromUrls")); assertEquals(false, FieldUtils.getFieldValue(allFilters.get(10), "objectDefinitionSource.stripQueryStringFromUrls")); } @@ -149,7 +152,6 @@ public class HttpSecurityBeanDefinitionParserTests { // These will be matched by the default pattern "/**" checkAutoConfigFilters(getFilters("/secure")); checkAutoConfigFilters(getFilters("/ImCaughtByTheUniversalMatchPattern")); - } @Test diff --git a/core/src/test/java/org/springframework/security/util/FilterChainProxyTests.java b/core/src/test/java/org/springframework/security/util/FilterChainProxyTests.java index cf7aa708f4..3d9842df13 100644 --- a/core/src/test/java/org/springframework/security/util/FilterChainProxyTests.java +++ b/core/src/test/java/org/springframework/security/util/FilterChainProxyTests.java @@ -62,20 +62,16 @@ public class FilterChainProxyTests { } } - @Test + @Test(expected=IllegalArgumentException.class) public void testDetectsFilterInvocationDefinitionSourceThatDoesNotReturnAllConfigAttributes() throws Exception { FilterChainProxy filterChainProxy = new FilterChainProxy(); filterChainProxy.setApplicationContext(new StaticApplicationContext()); - try { - filterChainProxy.setFilterInvocationDefinitionSource(new MockFilterInvocationDefinitionSource(false, false)); - filterChainProxy.afterPropertiesSet(); - fail("Should have thrown IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + filterChainProxy.setFilterInvocationDefinitionSource(new MockFilterInvocationDefinitionSource(false, false)); + filterChainProxy.afterPropertiesSet(); } - @Test + @Test(expected=IllegalArgumentException.class) public void testDetectsIfConfigAttributeDoesNotReturnValueForGetAttributeMethod() throws Exception { FilterChainProxy filterChainProxy = new FilterChainProxy(); filterChainProxy.setApplicationContext(new StaticApplicationContext()); @@ -89,12 +85,8 @@ public class FilterChainProxyTests { filterChainProxy.setFilterInvocationDefinitionSource(fids); - try { - filterChainProxy.afterPropertiesSet(); - filterChainProxy.init(new MockFilterConfig()); - fail("Should have thrown IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } + filterChainProxy.afterPropertiesSet(); + filterChainProxy.init(new MockFilterConfig()); } @Test(expected = IllegalArgumentException.class) @@ -131,18 +123,18 @@ public class FilterChainProxyTests { } } - @Test + @Test public void normalOperation() throws Exception { FilterChainProxy filterChainProxy = (FilterChainProxy) appCtx.getBean("filterChain", FilterChainProxy.class); doNormalOperation(filterChainProxy); } - @Test + @Test public void proxyPathWithoutLowerCaseConversionShouldntMatchDifferentCasePath() throws Exception { FilterChainProxy filterChainProxy = (FilterChainProxy) appCtx.getBean("filterChainNonLowerCase", FilterChainProxy.class); assertNull(filterChainProxy.getFilters("/some/other/path/blah")); } - + @Test public void normalOperationWithNewConfig() throws Exception { FilterChainProxy filterChainProxy = (FilterChainProxy) appCtx.getBean("newFilterChainProxy", FilterChainProxy.class); @@ -164,6 +156,24 @@ public class FilterChainProxyTests { doNormalOperation(filterChainProxy); } + @Test + public void pathWithNoMatchHasNoFilters() throws Exception { + FilterChainProxy filterChainProxy = (FilterChainProxy) appCtx.getBean("newFilterChainProxyNoDefaultPath", FilterChainProxy.class); + assertEquals(null, filterChainProxy.getFilters("/nomatch")); + } + + @Test + public void urlStrippingPropertyIsRespected() throws Exception { + FilterChainProxy filterChainProxy = (FilterChainProxy) appCtx.getBean("newFilterChainProxyNoDefaultPath", FilterChainProxy.class); + + // Should only match if we are stripping the query string + String url = "/blah.bar?x=something"; + assertNotNull(filterChainProxy.getFilters(url)); + assertEquals(2, filterChainProxy.getFilters(url).size()); + filterChainProxy.setStripQueryStringFromUrls(false); + assertNull(filterChainProxy.getFilters(url)); + } + private void checkPathAndFilterOrder(FilterChainProxy filterChainProxy) throws Exception { List filters = filterChainProxy.getFilters("/foo/blah"); assertEquals(1, filters.size()); diff --git a/core/src/test/resources/org/springframework/security/util/filtertest-valid.xml b/core/src/test/resources/org/springframework/security/util/filtertest-valid.xml index 12c9ff3f70..35d12b18fb 100644 --- a/core/src/test/resources/org/springframework/security/util/filtertest-valid.xml +++ b/core/src/test/resources/org/springframework/security/util/filtertest-valid.xml @@ -24,9 +24,9 @@ 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"> - + - + @@ -41,11 +41,11 @@ http://www.springframework.org/schema/security http://www.springframework.org/sc - + - CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON - PATTERN_TYPE_APACHE_ANT + CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON + PATTERN_TYPE_APACHE_ANT /foo/**=mockFilter /some/other/path/**=mockFilter /do/not/filter=#NONE# @@ -53,10 +53,10 @@ http://www.springframework.org/schema/security http://www.springframework.org/sc - + - PATTERN_TYPE_APACHE_ANT + PATTERN_TYPE_APACHE_ANT /foo/**=mockFilter /SOME/other/path/**=sif,mockFilter,mockFilter2 /do/not/filter=#NONE# @@ -73,6 +73,13 @@ http://www.springframework.org/schema/security http://www.springframework.org/sc + + + + + + + @@ -117,7 +124,7 @@ http://www.springframework.org/schema/security http://www.springframework.org/sc - +