Browse Source

OPEN - issue SEC-953: Query string isn't ignored while url - filterchain pattern matching
http://jira.springframework.org/browse/SEC-953. Added stripQueryStringFromUrls parameter to FilterChainProxy which works the same as the one on DefaultFilterInvocationDefinitionSource. This defaults to true when used with ant path matching.

Luke Taylor 17 years ago
parent
commit
39a656eb78

+ 121 - 120
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 <http> 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;
     }
-    
+
 }

+ 21 - 2
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[");

+ 3 - 1
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

+ 27 - 17
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());

+ 15 - 8
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">
 
-	<bean id="mockFilter" class="org.springframework.security.util.MockFilter"/>
+    <bean id="mockFilter" class="org.springframework.security.util.MockFilter"/>
 
-	<bean id="mockFilter2" class="org.springframework.security.util.MockFilter"/>
+    <bean id="mockFilter2" class="org.springframework.security.util.MockFilter"/>
 
     <!-- These are just here so we have filters of a specfic type to check the ordering is as expected -->
     <bean id="sif" class="org.springframework.security.context.HttpSessionContextIntegrationFilter"/>
@@ -41,11 +41,11 @@ http://www.springframework.org/schema/security http://www.springframework.org/sc
 
     <bean id="mockNotAFilter" class="org.springframework.security.util.MockNotAFilter"/>
 
-	<bean id="filterChain" class="org.springframework.security.util.FilterChainProxy">
+    <bean id="filterChain" class="org.springframework.security.util.FilterChainProxy">
       <property name="filterInvocationDefinitionSource">
          <value>
-		    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
       </property>
     </bean>
 
-	<bean id="filterChainNonLowerCase" class="org.springframework.security.util.FilterChainProxy">
+    <bean id="filterChainNonLowerCase" class="org.springframework.security.util.FilterChainProxy">
       <property name="filterInvocationDefinitionSource">
          <value>
-		    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
         </sec:filter-chain-map>
     </bean>
 
+    <bean id="newFilterChainProxyNoDefaultPath" class="org.springframework.security.util.FilterChainProxy">
+        <sec:filter-chain-map path-type="ant">
+            <sec:filter-chain pattern="/foo/**" filters="mockFilter"/>
+            <sec:filter-chain pattern="/*.bar" filters="mockFilter,mockFilter2"/>
+        </sec:filter-chain-map>
+    </bean>
+
     <bean id="newFilterChainProxyWrongPathOrder" class="org.springframework.security.util.FilterChainProxy">
         <sec:filter-chain-map path-type="ant">
             <sec:filter-chain pattern="/foo/**" filters="mockFilter"/>
@@ -117,7 +124,7 @@ http://www.springframework.org/schema/security http://www.springframework.org/sc
                       <ref local="apf"/>
                       <ref local="mockFilter"/>
                     </list>
-                </entry>                
+                </entry>
             </map>
         </property>
     </bean>