Explorar o código

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 %!s(int64=17) %!d(string=hai) anos
pai
achega
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>