SEC-764: Added support for "position" attribute. Also added "LAST" as an option for filter position.
This commit is contained in:
parent
d9fc36da41
commit
3b3d339393
|
@ -1,5 +1,9 @@
|
||||||
package org.springframework.security.config;
|
package org.springframework.security.config;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||||
|
@ -13,14 +17,10 @@ import org.springframework.security.userdetails.UserDetailsService;
|
||||||
import org.springframework.security.vote.AffirmativeBased;
|
import org.springframework.security.vote.AffirmativeBased;
|
||||||
import org.springframework.security.vote.AuthenticatedVoter;
|
import org.springframework.security.vote.AuthenticatedVoter;
|
||||||
import org.springframework.security.vote.RoleVoter;
|
import org.springframework.security.vote.RoleVoter;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
import org.apache.commons.logging.Log;
|
|
||||||
import org.apache.commons.logging.LogFactory;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utitily methods used internally by the Spring Security namespace configuration code.
|
* Utility methods used internally by the Spring Security namespace configuration code.
|
||||||
*
|
*
|
||||||
* @author Luke Taylor
|
* @author Luke Taylor
|
||||||
* @author Ben Alex
|
* @author Ben Alex
|
||||||
|
@ -44,6 +44,18 @@ public abstract class ConfigUtils {
|
||||||
parserContext.getRegistry().registerBeanDefinition(BeanIds.ACCESS_MANAGER, accessMgr);
|
parserContext.getRegistry().registerBeanDefinition(BeanIds.ACCESS_MANAGER, accessMgr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int countNonEmpty(String[] objects) {
|
||||||
|
int nonNulls = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < objects.length; i++) {
|
||||||
|
if (StringUtils.hasText(objects[i])) {
|
||||||
|
nonNulls++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nonNulls;
|
||||||
|
}
|
||||||
|
|
||||||
public static void addVoter(BeanDefinition voter, ParserContext parserContext) {
|
public static void addVoter(BeanDefinition voter, ParserContext parserContext) {
|
||||||
registerDefaultAccessManagerIfNecessary(parserContext);
|
registerDefaultAccessManagerIfNecessary(parserContext);
|
||||||
|
|
|
@ -1,25 +1,25 @@
|
||||||
package org.springframework.security.config;
|
package org.springframework.security.config;
|
||||||
|
|
||||||
import org.springframework.security.ui.FilterChainOrder;
|
import java.io.IOException;
|
||||||
import org.springframework.beans.factory.xml.BeanDefinitionDecorator;
|
|
||||||
import org.springframework.beans.factory.xml.ParserContext;
|
|
||||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
|
||||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
|
||||||
import org.springframework.core.Ordered;
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
import org.springframework.util.Assert;
|
|
||||||
|
|
||||||
import org.w3c.dom.Node;
|
|
||||||
import org.w3c.dom.Element;
|
|
||||||
|
|
||||||
import javax.servlet.Filter;
|
import javax.servlet.Filter;
|
||||||
|
import javax.servlet.FilterChain;
|
||||||
import javax.servlet.FilterConfig;
|
import javax.servlet.FilterConfig;
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.ServletRequest;
|
import javax.servlet.ServletRequest;
|
||||||
import javax.servlet.ServletResponse;
|
import javax.servlet.ServletResponse;
|
||||||
import javax.servlet.FilterChain;
|
|
||||||
import java.io.IOException;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
|
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||||
|
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||||
|
import org.springframework.beans.factory.xml.BeanDefinitionDecorator;
|
||||||
|
import org.springframework.beans.factory.xml.ParserContext;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.security.ui.FilterChainOrder;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
import org.w3c.dom.Node;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replaces a Spring bean of type "Filter" with a wrapper class which implements the <tt>Ordered</tt>
|
* Replaces a Spring bean of type "Filter" with a wrapper class which implements the <tt>Ordered</tt>
|
||||||
|
@ -33,6 +33,7 @@ public class OrderedFilterBeanDefinitionDecorator implements BeanDefinitionDecor
|
||||||
|
|
||||||
public static final String ATT_AFTER = "after";
|
public static final String ATT_AFTER = "after";
|
||||||
public static final String ATT_BEFORE = "before";
|
public static final String ATT_BEFORE = "before";
|
||||||
|
public static final String ATT_POSITION = "position";
|
||||||
|
|
||||||
public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder holder, ParserContext parserContext) {
|
public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder holder, ParserContext parserContext) {
|
||||||
Element elt = (Element)node;
|
Element elt = (Element)node;
|
||||||
|
@ -56,7 +57,17 @@ public class OrderedFilterBeanDefinitionDecorator implements BeanDefinitionDecor
|
||||||
private String getOrder(Element elt, ParserContext pc) {
|
private String getOrder(Element elt, ParserContext pc) {
|
||||||
String after = elt.getAttribute(ATT_AFTER);
|
String after = elt.getAttribute(ATT_AFTER);
|
||||||
String before = elt.getAttribute(ATT_BEFORE);
|
String before = elt.getAttribute(ATT_BEFORE);
|
||||||
|
String position = elt.getAttribute(ATT_POSITION);
|
||||||
|
|
||||||
|
if(ConfigUtils.countNonEmpty(new String[] {after, before, position}) != 1) {
|
||||||
|
pc.getReaderContext().error("A single '" + ATT_AFTER + "', '" + ATT_BEFORE + "', or '" +
|
||||||
|
ATT_POSITION + "' attribute must be supplied", pc.extractSource(elt));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.hasText(position)) {
|
||||||
|
return Integer.toString(FilterChainOrder.getOrder(position));
|
||||||
|
}
|
||||||
|
|
||||||
if (StringUtils.hasText(after)) {
|
if (StringUtils.hasText(after)) {
|
||||||
return Integer.toString(FilterChainOrder.getOrder(after) + 1);
|
return Integer.toString(FilterChainOrder.getOrder(after) + 1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ public abstract class FilterChainOrder {
|
||||||
private static final Map filterNameToOrder = new LinkedHashMap();
|
private static final Map filterNameToOrder = new LinkedHashMap();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
filterNameToOrder.put("FIRST", new Integer(FILTER_CHAIN_FIRST));
|
filterNameToOrder.put("FIRST", Integer.MIN_VALUE);
|
||||||
filterNameToOrder.put("CHANNEL_FILTER", new Integer(CHANNEL_FILTER));
|
filterNameToOrder.put("CHANNEL_FILTER", new Integer(CHANNEL_FILTER));
|
||||||
filterNameToOrder.put("CONCURRENT_SESSION_FILTER", new Integer(CONCURRENT_SESSION_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("SESSION_CONTEXT_INTEGRATION_FILTER", new Integer(HTTP_SESSION_CONTEXT_FILTER));
|
||||||
|
@ -61,6 +61,7 @@ public abstract class FilterChainOrder {
|
||||||
filterNameToOrder.put("NTLM_FILTER", new Integer(NTLM_FILTER));
|
filterNameToOrder.put("NTLM_FILTER", new Integer(NTLM_FILTER));
|
||||||
filterNameToOrder.put("FILTER_SECURITY_INTERCEPTOR", new Integer(FILTER_SECURITY_INTERCEPTOR));
|
filterNameToOrder.put("FILTER_SECURITY_INTERCEPTOR", new Integer(FILTER_SECURITY_INTERCEPTOR));
|
||||||
filterNameToOrder.put("SWITCH_USER_FILTER", new Integer(SWITCH_USER_FILTER));
|
filterNameToOrder.put("SWITCH_USER_FILTER", new Integer(SWITCH_USER_FILTER));
|
||||||
|
filterNameToOrder.put("LAST", Integer.MAX_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Allows filters to be used by name in the XSD file without explicit reference to Java constants */
|
/** Allows filters to be used by name in the XSD file without explicit reference to Java constants */
|
||||||
|
|
|
@ -420,14 +420,19 @@ any-user-service = user-service | jdbc-user-service | ldap-user-service
|
||||||
|
|
||||||
custom-filter =
|
custom-filter =
|
||||||
## 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.
|
## 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 custom-filter {after | before}?
|
element custom-filter {after | before | position}?
|
||||||
after =
|
after =
|
||||||
## The filter immediately after which the custom-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 after which the custom-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 {named-security-filter}
|
attribute after {named-security-filter}
|
||||||
before =
|
before =
|
||||||
## The filter immediately before which the custom-filter should be placed in the chain
|
## The filter immediately before which the custom-filter should be placed in the chain
|
||||||
attribute before {named-security-filter}
|
attribute before {named-security-filter}
|
||||||
|
position =
|
||||||
|
## The explicit position at which the custom-filter should be placed in the chain. Use if you are replacing a standard filter.
|
||||||
|
attribute position {named-security-filter}
|
||||||
|
|
||||||
named-security-filter = "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"
|
|
||||||
|
|
||||||
|
named-security-filter = "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" | "LAST"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1173,6 +1173,12 @@
|
||||||
in the chain</xs:documentation>
|
in the chain</xs:documentation>
|
||||||
</xs:annotation>
|
</xs:annotation>
|
||||||
</xs:attribute>
|
</xs:attribute>
|
||||||
|
<xs:attribute name="position" type="security:named-security-filter">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>The explicit position at which the custom-filter should be placed in the
|
||||||
|
chain. Use if you are replacing a standard filter.</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
<xs:attributeGroup name="after">
|
<xs:attributeGroup name="after">
|
||||||
|
@ -1194,6 +1200,14 @@
|
||||||
</xs:annotation>
|
</xs:annotation>
|
||||||
</xs:attribute>
|
</xs:attribute>
|
||||||
</xs:attributeGroup>
|
</xs:attributeGroup>
|
||||||
|
<xs:attributeGroup name="position">
|
||||||
|
<xs:attribute name="position" use="required" type="security:named-security-filter">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>The explicit position at which the custom-filter should be placed in the
|
||||||
|
chain. Use if you are replacing a standard filter.</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
|
</xs:attributeGroup>
|
||||||
<xs:simpleType name="named-security-filter">
|
<xs:simpleType name="named-security-filter">
|
||||||
<xs:restriction base="xs:token">
|
<xs:restriction base="xs:token">
|
||||||
<xs:enumeration value="FIRST"/>
|
<xs:enumeration value="FIRST"/>
|
||||||
|
@ -1213,6 +1227,7 @@
|
||||||
<xs:enumeration value="NTLM_FILTER"/>
|
<xs:enumeration value="NTLM_FILTER"/>
|
||||||
<xs:enumeration value="FILTER_SECURITY_INTERCEPTOR"/>
|
<xs:enumeration value="FILTER_SECURITY_INTERCEPTOR"/>
|
||||||
<xs:enumeration value="SWITCH_USER_FILTER"/>
|
<xs:enumeration value="SWITCH_USER_FILTER"/>
|
||||||
|
<xs:enumeration value="LAST"/>
|
||||||
</xs:restriction>
|
</xs:restriction>
|
||||||
</xs:simpleType>
|
</xs:simpleType>
|
||||||
</xs:schema>
|
</xs:schema>
|
||||||
|
|
|
@ -248,12 +248,17 @@ public class HttpSecurityBeanDefinitionParserTests {
|
||||||
"<b:bean id='userFilter' class='org.springframework.security.util.MockFilter'>" +
|
"<b:bean id='userFilter' class='org.springframework.security.util.MockFilter'>" +
|
||||||
" <custom-filter after='SESSION_CONTEXT_INTEGRATION_FILTER'/>" +
|
" <custom-filter after='SESSION_CONTEXT_INTEGRATION_FILTER'/>" +
|
||||||
"</b:bean>" +
|
"</b:bean>" +
|
||||||
"<b:bean id='userFilter2' class='org.springframework.security.util.MockFilter'/>");
|
"<b:bean id='userFilter2' class='org.springframework.security.util.MockFilter'>" +
|
||||||
|
" <custom-filter position='FIRST'/>" +
|
||||||
|
"</b:bean>" +
|
||||||
|
"<b:bean id='userFilter3' class='org.springframework.security.util.MockFilter'/>");
|
||||||
List filters = getFilters("/someurl");
|
List filters = getFilters("/someurl");
|
||||||
|
|
||||||
assertEquals(12, filters.size());
|
assertEquals(13, filters.size());
|
||||||
assertTrue(filters.get(1) instanceof OrderedFilterBeanDefinitionDecorator.OrderedFilterDecorator);
|
assertTrue(filters.get(0) instanceof OrderedFilterBeanDefinitionDecorator.OrderedFilterDecorator);
|
||||||
assertEquals("userFilter", ((OrderedFilterBeanDefinitionDecorator.OrderedFilterDecorator)filters.get(1)).getBeanName());
|
assertTrue(filters.get(2) instanceof OrderedFilterBeanDefinitionDecorator.OrderedFilterDecorator);
|
||||||
|
assertEquals("userFilter", ((OrderedFilterBeanDefinitionDecorator.OrderedFilterDecorator)filters.get(2)).getBeanName());
|
||||||
|
assertEquals("userFilter2", ((OrderedFilterBeanDefinitionDecorator.OrderedFilterDecorator)filters.get(0)).getBeanName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
Loading…
Reference in New Issue