diff --git a/core/src/main/java/org/springframework/security/config/OrderedFilterBeanDefinitionDecorator.java b/core/src/main/java/org/springframework/security/config/OrderedFilterBeanDefinitionDecorator.java index 5b925e518f..787e0a2ead 100644 --- a/core/src/main/java/org/springframework/security/config/OrderedFilterBeanDefinitionDecorator.java +++ b/core/src/main/java/org/springframework/security/config/OrderedFilterBeanDefinitionDecorator.java @@ -1,5 +1,7 @@ package org.springframework.security.config; +import org.springframework.security.util.FieldUtils; +import org.springframework.security.ui.FilterChainOrder; import org.springframework.beans.factory.xml.BeanDefinitionDecorator; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.beans.factory.config.BeanDefinitionHolder; @@ -8,6 +10,7 @@ import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.core.Ordered; import org.springframework.util.StringUtils; import org.springframework.util.Assert; +import org.springframework.util.ReflectionUtils; import org.w3c.dom.Node; import org.w3c.dom.Element; @@ -30,11 +33,13 @@ import java.io.IOException; */ public class OrderedFilterBeanDefinitionDecorator implements BeanDefinitionDecorator { - public static final String ATT_ORDER = "order"; + public static final String ATT_AFTER = "after"; + public static final String ATT_BEFORE = "before"; public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder holder, ParserContext parserContext) { Element elt = (Element)node; - String order = elt.getAttribute(ATT_ORDER); + String order = getOrder(elt, parserContext); + BeanDefinition filter = holder.getBeanDefinition(); BeanDefinition wrapper = new RootBeanDefinition(OrderedFilterDecorator.class); wrapper.getConstructorArgumentValues().addIndexedArgumentValue(0, holder.getBeanName()); @@ -47,6 +52,24 @@ public class OrderedFilterBeanDefinitionDecorator implements BeanDefinitionDecor return new BeanDefinitionHolder(wrapper, holder.getBeanName()); } + /** + * Attempts to get the order of the filter by parsing the 'before' or 'after' attributes. + */ + private String getOrder(Element elt, ParserContext pc) { + String after = elt.getAttribute(ATT_AFTER); + String before = elt.getAttribute(ATT_BEFORE); + + if (StringUtils.hasText(after)) { + return FilterChainOrder.getOrder(after).toString(); + } + + if (StringUtils.hasText(before)) { + return FilterChainOrder.getOrder(before).toString(); + } + + return null; + } + static class OrderedFilterDecorator implements Filter, Ordered { private Integer order = null; private Filter delegate; @@ -72,7 +95,7 @@ public class OrderedFilterBeanDefinitionDecorator implements BeanDefinitionDecor public final int getOrder() { if(order == null) { Assert.isInstanceOf(Ordered.class, "Filter '"+ beanName +"' must implement the 'Ordered' interface " + - " or you must specify an order in "); + " or you must specify one of the attributes 'after' or 'before' in "); return ((Ordered)delegate).getOrder(); } diff --git a/core/src/main/java/org/springframework/security/providers/anonymous/AnonymousProcessingFilter.java b/core/src/main/java/org/springframework/security/providers/anonymous/AnonymousProcessingFilter.java index 3a7e4c8bb4..28893e0456 100644 --- a/core/src/main/java/org/springframework/security/providers/anonymous/AnonymousProcessingFilter.java +++ b/core/src/main/java/org/springframework/security/providers/anonymous/AnonymousProcessingFilter.java @@ -122,7 +122,7 @@ public class AnonymousProcessingFilter extends SpringSecurityFilter implements } public int getOrder() { - return FilterChainOrder.ANON_PROCESSING_FILTER; + return FilterChainOrder.ANONYMOUS_FILTER; } public String getKey() { diff --git a/core/src/main/java/org/springframework/security/securechannel/ChannelProcessingFilter.java b/core/src/main/java/org/springframework/security/securechannel/ChannelProcessingFilter.java index 04bbe46fc6..011502080a 100644 --- a/core/src/main/java/org/springframework/security/securechannel/ChannelProcessingFilter.java +++ b/core/src/main/java/org/springframework/security/securechannel/ChannelProcessingFilter.java @@ -139,6 +139,6 @@ public class ChannelProcessingFilter extends SpringSecurityFilter implements Ini } public int getOrder() { - return FilterChainOrder.CHANNEL_PROCESSING_FILTER; + return FilterChainOrder.CHANNEL_FILTER; } } diff --git a/core/src/main/java/org/springframework/security/ui/FilterChainOrder.java b/core/src/main/java/org/springframework/security/ui/FilterChainOrder.java index 05c0087ed8..01ad8472e2 100644 --- a/core/src/main/java/org/springframework/security/ui/FilterChainOrder.java +++ b/core/src/main/java/org/springframework/security/ui/FilterChainOrder.java @@ -1,6 +1,10 @@ package org.springframework.security.ui; import org.springframework.core.Ordered; +import org.springframework.util.Assert; + +import java.util.Map; +import java.util.LinkedHashMap; /** * Stores the default order numbers of all Spring Security filters for use in configuration. @@ -17,20 +21,52 @@ public abstract class FilterChainOrder { private static final int INTERVAL = 100; private static int i = 1; - public static final int CHANNEL_PROCESSING_FILTER = FILTER_CHAIN_FIRST; + public static final int CHANNEL_FILTER = FILTER_CHAIN_FIRST; public static final int CONCURRENT_SESSION_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++; public static final int HTTP_SESSION_CONTEXT_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++; public static final int LOGOUT_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++; + public static final int X509_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++; public static final int PRE_AUTH_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++; public static final int CAS_PROCESSING_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++; - public static final int AUTH_PROCESSING_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++; + public static final int AUTHENTICATION_PROCESSING_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++; public static final int LOGIN_PAGE_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++; public static final int BASIC_PROCESSING_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++; - public static final int SECURITY_CONTEXT_HOLDER_AWARE_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++; + public static final int SERVLET_API_SUPPORT_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++; public static final int REMEMBER_ME_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++; - public static final int ANON_PROCESSING_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++; + public static final int ANONYMOUS_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++; public static final int EXCEPTION_TRANSLATION_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++; public static final int NTLM_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++; public static final int FILTER_SECURITY_INTERCEPTOR = FILTER_CHAIN_FIRST + INTERVAL * i++; public static final int SWITCH_USER_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++; + + private static final Map filterNameToOrder = new LinkedHashMap(); + + static { + filterNameToOrder.put("FIRST", new Integer(FILTER_CHAIN_FIRST)); + filterNameToOrder.put("CHANNEL_FILTER", new Integer(CHANNEL_FILTER)); + filterNameToOrder.put("CONCURRENT_SESSION_FILTER", new Integer(CONCURRENT_SESSION_FILTER)); + filterNameToOrder.put("SESSION_CONTEXT_INTEGRATION_FILTER", new Integer(HTTP_SESSION_CONTEXT_FILTER)); + filterNameToOrder.put("LOGOUT_FILTER", new Integer(LOGOUT_FILTER)); + filterNameToOrder.put("X509_FILTER", new Integer(X509_FILTER)); + filterNameToOrder.put("PRE_AUTH_FILTER", new Integer(PRE_AUTH_FILTER)); + filterNameToOrder.put("CAS_PROCESSING_FILTER", new Integer(CAS_PROCESSING_FILTER)); + filterNameToOrder.put("AUTHENTICATION_PROCESSING_FILTER", new Integer(AUTHENTICATION_PROCESSING_FILTER)); + filterNameToOrder.put("BASIC_PROCESSING_FILTER", new Integer(BASIC_PROCESSING_FILTER)); + filterNameToOrder.put("SERVLET_API_SUPPORT_FILTER", new Integer(SERVLET_API_SUPPORT_FILTER)); + filterNameToOrder.put("REMEMBER_ME_FILTER", new Integer(REMEMBER_ME_FILTER)); + filterNameToOrder.put("ANONYMOUS_FILTER", new Integer(ANONYMOUS_FILTER)); + filterNameToOrder.put("EXCEPTION_TRANSLATION_FILTER", new Integer(EXCEPTION_TRANSLATION_FILTER)); + filterNameToOrder.put("NTLM_FILTER", new Integer(NTLM_FILTER)); + filterNameToOrder.put("FILTER_SECURITY_INTERCEPTOR", new Integer(FILTER_SECURITY_INTERCEPTOR)); + filterNameToOrder.put("SWITCH_USER_FILTER", new Integer(SWITCH_USER_FILTER)); + } + + /** Allows filters to be used by name in the XSD file without explicit reference to Java constants */ + public static Integer getOrder(String filterName) { + Integer order = (Integer) filterNameToOrder.get(filterName); + + Assert.notNull(order, "Unable to match filter name " + filterName); + + return order; + } } diff --git a/core/src/main/java/org/springframework/security/ui/SpringSecurityFilter.java b/core/src/main/java/org/springframework/security/ui/SpringSecurityFilter.java index 3c49fdd12b..5c1ab09a7c 100644 --- a/core/src/main/java/org/springframework/security/ui/SpringSecurityFilter.java +++ b/core/src/main/java/org/springframework/security/ui/SpringSecurityFilter.java @@ -51,4 +51,8 @@ public abstract class SpringSecurityFilter implements Filter, Ordered { } protected abstract void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException; + + public String toString() { + return getClass() + "[ order=" + getOrder() + "; ]"; + } } \ No newline at end of file diff --git a/core/src/main/java/org/springframework/security/ui/webapp/AuthenticationProcessingFilter.java b/core/src/main/java/org/springframework/security/ui/webapp/AuthenticationProcessingFilter.java index 9c6cb01997..644ed6efe6 100644 --- a/core/src/main/java/org/springframework/security/ui/webapp/AuthenticationProcessingFilter.java +++ b/core/src/main/java/org/springframework/security/ui/webapp/AuthenticationProcessingFilter.java @@ -154,7 +154,7 @@ public class AuthenticationProcessingFilter extends AbstractProcessingFilter { } public int getOrder() { - return FilterChainOrder.AUTH_PROCESSING_FILTER; + return FilterChainOrder.AUTHENTICATION_PROCESSING_FILTER; } String getUsernameParameter() { diff --git a/core/src/main/java/org/springframework/security/wrapper/SecurityContextHolderAwareRequestFilter.java b/core/src/main/java/org/springframework/security/wrapper/SecurityContextHolderAwareRequestFilter.java index d13d51be35..fd525fcec1 100644 --- a/core/src/main/java/org/springframework/security/wrapper/SecurityContextHolderAwareRequestFilter.java +++ b/core/src/main/java/org/springframework/security/wrapper/SecurityContextHolderAwareRequestFilter.java @@ -92,6 +92,6 @@ public class SecurityContextHolderAwareRequestFilter extends SpringSecurityFilte } public int getOrder() { - return FilterChainOrder.SECURITY_CONTEXT_HOLDER_AWARE_FILTER; + return FilterChainOrder.SERVLET_API_SUPPORT_FILTER; } } diff --git a/core/src/main/resources/org/springframework/security/config/spring-security-2.0.rnc b/core/src/main/resources/org/springframework/security/config/spring-security-2.0.rnc index ca20abcae7..cefe55f8cd 100644 --- a/core/src/main/resources/org/springframework/security/config/spring-security-2.0.rnc +++ b/core/src/main/resources/org/springframework/security/config/spring-security-2.0.rnc @@ -267,9 +267,15 @@ jdbc-user-service.attlist &= user-filter = - ## Used to indicate that a filter bean declaration should be incorporated into the security filter chain. - element user-filter {user-filter.attlist} -user-filter.attlist &= - ## The order value for the chain (user to position the filter relative to the standard filters) - attribute order {xsd:integer}? + ## Used to indicate that a filter bean declaration should be incorporated into the security filter chain. If neither the 'after' or 'before' options are supplied, then the filter must implement the Ordered interface directly. + element user-filter {after | before}? +after = + ## The filter immediately after which the user-filter should be placed in the chain. This feature will only be needed by advanced users who wish to mix their own filters into the security filter chain and have some knowledge of the standard Spring Security filters. The filter names map to specific Spring Security implementation filters. + attribute after {"FIRST" | "CHANNEL_FILTER" | "CONCURRENT_SESSION_FILTER" | "SESSION_CONTEXT_INTEGRATION_FILTER" | "LOGOUT_FILTER" | "X509_FILTER" | "PRE_AUTH_FILTER" | "CAS_PROCESSING_FILTER" | "AUTHENTICATION_PROCESSING_FILTER" | "BASIC_PROCESSING_FILTER" | "SERVLET_API_SUPPORT_FILTER" | "REMEMBER_ME_FILTER" | "ANONYMOUS_FILTER" | "EXCEPTION_TRANSLATION_FILTER" | "NTLM_FILTER" | "FILTER_SECURITY_INTERCEPTOR" | "SWITCH_USER_FILTER"} +before = + ## The filter immediately before which the user-filter should be placed in the chain + attribute before {"FIRST" | "CHANNEL_FILTER" | "CONCURRENT_SESSION_FILTER" | "SESSION_CONTEXT_INTEGRATION_FILTER" | "LOGOUT_FILTER" | "X509_FILTER" | "PRE_AUTH_FILTER" | "CAS_PROCESSING_FILTER" | "AUTHENTICATION_PROCESSING_FILTER" | "BASIC_PROCESSING_FILTER" | "SERVLET_API_SUPPORT_FILTER" | "REMEMBER_ME_FILTER" | "ANONYMOUS_FILTER" | "EXCEPTION_TRANSLATION_FILTER" | "NTLM_FILTER" | "FILTER_SECURITY_INTERCEPTOR" | "SWITCH_USER_FILTER"} + + + diff --git a/core/src/main/resources/org/springframework/security/config/spring-security-2.0.xsd b/core/src/main/resources/org/springframework/security/config/spring-security-2.0.xsd index c3682123d3..f9e6045e3c 100644 --- a/core/src/main/resources/org/springframework/security/config/spring-security-2.0.xsd +++ b/core/src/main/resources/org/springframework/security/config/spring-security-2.0.xsd @@ -647,19 +647,124 @@ + + + + + - Used to indicate that a filter bean declaration should be incorporated into the security filter chain. + Used to indicate that a filter bean declaration should be incorporated into the security filter chain. If neither the 'after' or 'before' options are supplied, then the filter must implement the Ordered interface directly. - + + + The filter immediately after which the user-filter should be placed in the chain. This feature will only be needed by advanced users who wish to mix their own filters into the security filter chain and have some knowledge of the standard Spring Security filters. The filter names map to specific Spring Security implementation filters. + + + + + + + + + + + + + + + + + + + + + + + + + + The filter immediately before which the user-filter should be placed in the chain + + + + + + + + + + + + + + + + + + + + + + + - - + + - The order value for the chain (user to position the filter relative to the standard filters) + The filter immediately after which the user-filter should be placed in the chain. This feature will only be needed by advanced users who wish to mix their own filters into the security filter chain and have some knowledge of the standard Spring Security filters. The filter names map to specific Spring Security implementation filters. + + + + + + + + + + + + + + + + + + + + + + + + + + + The filter immediately before which the user-filter should be placed in the chain + + + + + + + + + + + + + + + + + + + + + + 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 de3e8a98cf..2bf071d978 100644 --- a/core/src/test/java/org/springframework/security/config/HttpSecurityBeanDefinitionParserTests.java +++ b/core/src/test/java/org/springframework/security/config/HttpSecurityBeanDefinitionParserTests.java @@ -146,7 +146,7 @@ public class HttpSecurityBeanDefinitionParserTests { setContext( " " + " " + - " " + + " " + " " + " " + AUTH_PROVIDER_XML); @@ -199,14 +199,14 @@ public class HttpSecurityBeanDefinitionParserTests { setContext( "" + AUTH_PROVIDER_XML + "" + - " " + + " " + "" + ""); List filters = getFilterChainProxy().getFilters("/someurl"); assertEquals(11, filters.size()); - assertTrue(filters.get(10) instanceof OrderedFilterBeanDefinitionDecorator.OrderedFilterDecorator); - assertEquals("userFilter", ((OrderedFilterBeanDefinitionDecorator.OrderedFilterDecorator)filters.get(10)).getBeanName()); + assertTrue(filters.get(1) instanceof OrderedFilterBeanDefinitionDecorator.OrderedFilterDecorator); + assertEquals("userFilter", ((OrderedFilterBeanDefinitionDecorator.OrderedFilterDecorator)filters.get(1)).getBeanName()); } @Test diff --git a/core/src/test/java/org/springframework/security/providers/x509/X509TestUtils.java b/core/src/test/java/org/springframework/security/providers/x509/X509TestUtils.java index 251df4c72c..7fca07558d 100644 --- a/core/src/test/java/org/springframework/security/providers/x509/X509TestUtils.java +++ b/core/src/test/java/org/springframework/security/providers/x509/X509TestUtils.java @@ -68,8 +68,7 @@ public class X509TestUtils { * [signature omitted] * */ - public static X509Certificate buildTestCertificate() - throws Exception { + public static X509Certificate buildTestCertificate() throws Exception { String cert = "-----BEGIN CERTIFICATE-----\n" + "MIIEQTCCAymgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBkzEaMBgGA1UEAxMRTW9u\n" + "a2V5IE1hY2hpbmUgQ0ExCzAJBgNVBAYTAlVLMREwDwYDVQQIEwhTY290bGFuZDEQ\n"