SEC-632: Added user-filter element to namespace to allow a user to add their filters. Filters which aren't in the org.security.springframework package will now be skipped. Also renamed FilterChainOrderUtils and members for future use in ordering (e.g. using "after", "before" as attributes in user-filter).

This commit is contained in:
Luke Taylor 2008-01-19 13:51:03 +00:00
parent c3cd5d98ba
commit 5e3a0ef379
25 changed files with 182 additions and 73 deletions

View File

@ -17,7 +17,7 @@ package org.springframework.security.concurrent;
import org.springframework.security.Authentication;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.security.ui.FilterChainOrderUtils;
import org.springframework.security.ui.FilterChainOrder;
import org.springframework.security.ui.SpringSecurityFilter;
import org.springframework.security.ui.logout.LogoutHandler;
import org.springframework.security.ui.logout.SecurityContextLogoutHandler;
@ -117,6 +117,6 @@ public class ConcurrentSessionFilter extends SpringSecurityFilter implements Ini
}
public int getOrder() {
return FilterChainOrderUtils.CONCURRENT_SESSION_FILTER_ORDER;
return FilterChainOrder.CONCURRENT_SESSION_FILTER;
}
}

View File

@ -28,5 +28,6 @@ abstract class Elements {
public static final String PASSWORD_ENCODER = "password-encoder";
public static final String SALT_SOURCE = "salt-source";
public static final String PORT_MAPPINGS = "port-mappings";
public static final String PORT_MAPPING = "port-mapping";
public static final String PORT_MAPPING = "port-mapping";
public static final String USER_FILTER = "user-filter";
}

View File

@ -183,9 +183,14 @@ public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor
continue;
}
// Filters must be Spring security filters or wrapped using <user-filter>
if (!filter.getClass().getName().startsWith("org.springframework.security")) {
continue;
}
if (!(filter instanceof Ordered)) {
// TODO: Possibly log this as a warning and skip this filter.
logger.info("Filter " + id + " doesn't implement the Ordered interface, skipping it.");
continue;
}
orderedFilters.add(filter);

View File

@ -0,0 +1,74 @@
package org.springframework.security.config;
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.RootBeanDefinition;
import org.springframework.core.Ordered;
import org.springframework.util.Assert;
import org.w3c.dom.Node;
import org.w3c.dom.Element;
import javax.servlet.Filter;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.FilterChain;
import java.io.IOException;
/**
* Replaces a Spring bean of type "Filter" with a wrapper class which implement the <tt>Ordered</tt>
* interface. This allows user to add their own filter to the security chain.
*
* @author Luke Taylor
* @version $Id$
*/
public class OrderedFilterBeanDefinitionDecorator implements BeanDefinitionDecorator {
public static final String ATT_ORDER = "order";
public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder holder, ParserContext parserContext) {
Element elt = (Element)node;
String order = elt.getAttribute(ATT_ORDER);
BeanDefinition filter = holder.getBeanDefinition();
Assert.hasText(order, "order attribute is required");
BeanDefinition wrapper = new RootBeanDefinition(OrderedFilterDecorator.class);
wrapper.getConstructorArgumentValues().addGenericArgumentValue(filter);
wrapper.getPropertyValues().addPropertyValue("order", order);
return new BeanDefinitionHolder(wrapper, holder.getBeanName());
}
}
class OrderedFilterDecorator implements Filter, Ordered {
private int order = LOWEST_PRECEDENCE;
private Filter delegate;
OrderedFilterDecorator(Filter delegate) {
this.delegate = delegate;
}
public final void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
delegate.doFilter(request, response, chain);
}
public final void init(FilterConfig filterConfig) throws ServletException {
delegate.init(filterConfig);
}
public final void destroy() {
delegate.destroy();
}
public final int getOrder() {
return order;
}
public final void setOrder(int order) {
this.order = order;
}
}

View File

@ -24,5 +24,6 @@ public class SecurityNamespaceHandler extends NamespaceHandlerSupport {
// Decorators
registerBeanDefinitionDecorator(Elements.INTERCEPT_METHODS, new InterceptMethodsBeanDefinitionDecorator());
registerBeanDefinitionDecorator(Elements.FILTER_CHAIN_MAP, new FilterChainMapBeanDefinitionDecorator());
registerBeanDefinitionDecorator(Elements.USER_FILTER, new OrderedFilterBeanDefinitionDecorator());
}
}

View File

@ -31,7 +31,7 @@ import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import org.springframework.security.ui.SpringSecurityFilter;
import org.springframework.security.ui.FilterChainOrderUtils;
import org.springframework.security.ui.FilterChainOrder;
/**
* Populates the {@link SecurityContextHolder} with information obtained from
@ -442,7 +442,7 @@ public class HttpSessionContextIntegrationFilter extends SpringSecurityFilter im
}
public int getOrder() {
return FilterChainOrderUtils.HTTP_SESSION_CONTEXT_FILTER_ORDER;
return FilterChainOrder.HTTP_SESSION_CONTEXT_FILTER;
}
//~ Inner Classes ==================================================================================================

View File

@ -18,7 +18,7 @@ package org.springframework.security.intercept.web;
import org.springframework.security.intercept.AbstractSecurityInterceptor;
import org.springframework.security.intercept.InterceptorStatusToken;
import org.springframework.security.intercept.ObjectDefinitionSource;
import org.springframework.security.ui.FilterChainOrderUtils;
import org.springframework.security.ui.FilterChainOrder;
import org.springframework.core.Ordered;
import java.io.IOException;
@ -140,6 +140,6 @@ public class FilterSecurityInterceptor extends AbstractSecurityInterceptor imple
}
public int getOrder() {
return FilterChainOrderUtils.FILTER_SECURITY_INTERCEPTOR_ORDER;
return FilterChainOrder.FILTER_SECURITY_INTERCEPTOR;
}
}

View File

@ -21,7 +21,7 @@ import org.springframework.security.context.SecurityContextHolder;
import org.springframework.security.ui.AuthenticationDetailsSource;
import org.springframework.security.ui.AuthenticationDetailsSourceImpl;
import org.springframework.security.ui.FilterChainOrderUtils;
import org.springframework.security.ui.FilterChainOrder;
import org.springframework.security.ui.SpringSecurityFilter;
import org.springframework.security.userdetails.memory.UserAttribute;
@ -36,7 +36,6 @@ import org.springframework.util.Assert;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@ -123,7 +122,7 @@ public class AnonymousProcessingFilter extends SpringSecurityFilter implements
}
public int getOrder() {
return FilterChainOrderUtils.ANON_PROCESSING_FILTER_ORDER;
return FilterChainOrder.ANON_PROCESSING_FILTER;
}
public String getKey() {

View File

@ -20,7 +20,7 @@ import org.springframework.security.ConfigAttributeDefinition;
import org.springframework.security.intercept.web.FilterInvocation;
import org.springframework.security.intercept.web.FilterInvocationDefinitionSource;
import org.springframework.security.ui.SpringSecurityFilter;
import org.springframework.security.ui.FilterChainOrderUtils;
import org.springframework.security.ui.FilterChainOrder;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
@ -139,6 +139,6 @@ public class ChannelProcessingFilter extends SpringSecurityFilter implements Ini
}
public int getOrder() {
return FilterChainOrderUtils.CHANNEL_PROCESSING_FILTER_ORDER;
return FilterChainOrder.CHANNEL_PROCESSING_FILTER;
}
}

View File

@ -236,6 +236,6 @@ public class ExceptionTranslationFilter extends SpringSecurityFilter implements
}
public int getOrder() {
return FilterChainOrderUtils.EXCEPTION_TRANSLATION_FILTER_ORDER;
return FilterChainOrder.EXCEPTION_TRANSLATION_FILTER;
}
}

View File

@ -0,0 +1,34 @@
package org.springframework.security.ui;
import org.springframework.core.Ordered;
/**
* Stores the default order numbers of all Spring Security filters for use in configuration.
*
* @author Luke Taylor
* @version $Id$
*/
public abstract class FilterChainOrder {
/**
* The first position at which a Spring Security filter will be found. Any filter with an order less than this will
* be guaranteed to be placed before the Spring Security filters in the stack.
*/
public static final int FILTER_CHAIN_FIRST = Ordered.HIGHEST_PRECEDENCE + 1000;
private static final int INTERVAL = 100;
public static final int CHANNEL_PROCESSING_FILTER = FILTER_CHAIN_FIRST + INTERVAL;
public static final int CONCURRENT_SESSION_FILTER = FILTER_CHAIN_FIRST + INTERVAL * 2;
public static final int HTTP_SESSION_CONTEXT_FILTER = FILTER_CHAIN_FIRST + INTERVAL * 3;
public static final int LOGOUT_FILTER = FILTER_CHAIN_FIRST + INTERVAL * 4;
public static final int AUTH_PROCESSING_FILTER = FILTER_CHAIN_FIRST + INTERVAL * 5;
public static final int CAS_PROCESSING_FILTER = FILTER_CHAIN_FIRST + INTERVAL * 5;
public static final int LOGIN_PAGE_FILTER = FILTER_CHAIN_FIRST + INTERVAL * 6;
public static final int BASIC_PROCESSING_FILTER = FILTER_CHAIN_FIRST + INTERVAL * 7;
public static final int SECURITY_CONTEXT_HOLDER_AWARE_FILTER = FILTER_CHAIN_FIRST + INTERVAL * 8;
public static final int REMEMBER_ME_FILTER = FILTER_CHAIN_FIRST + INTERVAL * 9;
public static final int ANON_PROCESSING_FILTER = FILTER_CHAIN_FIRST + INTERVAL * 10;
public static final int EXCEPTION_TRANSLATION_FILTER = FILTER_CHAIN_FIRST + INTERVAL * 11;
public static final int NTLM_FILTER = FILTER_CHAIN_FIRST + INTERVAL * 12;
public static final int FILTER_SECURITY_INTERCEPTOR = FILTER_CHAIN_FIRST + INTERVAL * 13;
public static final int SWITCH_USER_FILTER = FILTER_CHAIN_FIRST + INTERVAL * 14;
}

View File

@ -1,34 +0,0 @@
package org.springframework.security.ui;
import org.springframework.core.Ordered;
/**
* Stores the default order numbers of all Spring Security filters for use in configuration.
*
* @author luke
* @version $Id$
*/
public abstract class FilterChainOrderUtils {
/**
* The first position at which a Spring Security filter will be found. Any filter with an order less than this will
* be guaranteed to be placed before the Spring Security filters in the stack.
*/
public static final int FILTER_CHAIN_FIRST = Ordered.HIGHEST_PRECEDENCE + 1000;
private static final int INTERVAL = 100;
public static final int CHANNEL_PROCESSING_FILTER_ORDER = FILTER_CHAIN_FIRST + INTERVAL;
public static final int CONCURRENT_SESSION_FILTER_ORDER = FILTER_CHAIN_FIRST + INTERVAL * 2;
public static final int HTTP_SESSION_CONTEXT_FILTER_ORDER = FILTER_CHAIN_FIRST + INTERVAL * 3;
public static final int LOGOUT_FILTER_ORDER = FILTER_CHAIN_FIRST + INTERVAL * 4;
public static final int AUTH_PROCESSING_FILTER_ORDER = FILTER_CHAIN_FIRST + INTERVAL * 5;
public static final int CAS_PROCESSING_FILTER_ORDER = FILTER_CHAIN_FIRST + INTERVAL * 5;
public static final int LOGIN_PAGE_FILTER_ORDER = FILTER_CHAIN_FIRST + INTERVAL * 6;
public static final int BASIC_PROCESSING_FILTER_ORDER = FILTER_CHAIN_FIRST + INTERVAL * 7;
public static final int SECURITY_CONTEXT_HOLDER_AWARE_FILTER_ORDER = FILTER_CHAIN_FIRST + INTERVAL * 8;
public static final int REMEMBER_ME_FILTER_ORDER = FILTER_CHAIN_FIRST + INTERVAL * 9;
public static final int ANON_PROCESSING_FILTER_ORDER = FILTER_CHAIN_FIRST + INTERVAL * 10;
public static final int EXCEPTION_TRANSLATION_FILTER_ORDER = FILTER_CHAIN_FIRST + INTERVAL * 11;
public static final int NTLM_FILTER_ORDER = FILTER_CHAIN_FIRST + INTERVAL * 12;
public static final int FILTER_SECURITY_INTERCEPTOR_ORDER = FILTER_CHAIN_FIRST + INTERVAL * 13;
public static final int SWITCH_USER_FILTER_ORDER = FILTER_CHAIN_FIRST + INTERVAL * 14;
}

View File

@ -35,7 +35,7 @@ import org.springframework.security.providers.anonymous.AnonymousAuthenticationT
import org.springframework.security.ui.AuthenticationDetailsSource;
import org.springframework.security.ui.AuthenticationDetailsSourceImpl;
import org.springframework.security.ui.AuthenticationEntryPoint;
import org.springframework.security.ui.FilterChainOrderUtils;
import org.springframework.security.ui.FilterChainOrder;
import org.springframework.security.ui.SpringSecurityFilter;
import org.springframework.security.ui.rememberme.RememberMeServices;
import org.springframework.util.Assert;
@ -223,6 +223,6 @@ public class BasicProcessingFilter extends SpringSecurityFilter implements Initi
}
public int getOrder() {
return FilterChainOrderUtils.BASIC_PROCESSING_FILTER_ORDER;
return FilterChainOrder.BASIC_PROCESSING_FILTER;
}
}

View File

@ -21,10 +21,8 @@ import org.springframework.security.AuthenticationException;
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
import org.springframework.security.ui.AbstractProcessingFilter;
import org.springframework.security.ui.FilterChainOrderUtils;
import org.springframework.security.ui.FilterChainOrder;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
@ -87,6 +85,6 @@ public class CasProcessingFilter extends AbstractProcessingFilter {
}
public int getOrder() {
return FilterChainOrderUtils.CAS_PROCESSING_FILTER_ORDER;
return FilterChainOrder.CAS_PROCESSING_FILTER;
}
}

View File

@ -17,18 +17,14 @@ package org.springframework.security.ui.logout;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.Authentication;
import org.springframework.security.ui.SpringSecurityFilter;
import org.springframework.security.ui.FilterChainOrderUtils;
import org.springframework.security.ui.FilterChainOrder;
import org.springframework.security.util.RedirectUtils;
import org.springframework.security.context.SecurityContextHolder;
import org.apache.commons.logging.Log;
@ -158,6 +154,6 @@ public class LogoutFilter extends SpringSecurityFilter {
}
public int getOrder() {
return FilterChainOrderUtils.LOGOUT_FILTER_ORDER;
return FilterChainOrder.LOGOUT_FILTER;
}
}

View File

@ -20,7 +20,7 @@ import org.springframework.security.AuthenticationException;
import org.springframework.security.AuthenticationManager;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.security.event.authentication.InteractiveAuthenticationSuccessEvent;
import org.springframework.security.ui.FilterChainOrderUtils;
import org.springframework.security.ui.FilterChainOrder;
import org.springframework.security.ui.SpringSecurityFilter;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationEventPublisher;
@ -159,6 +159,6 @@ public class RememberMeProcessingFilter extends SpringSecurityFilter implements
}
public int getOrder() {
return FilterChainOrderUtils.REMEMBER_ME_FILTER_ORDER;
return FilterChainOrder.REMEMBER_ME_FILTER;
}
}

View File

@ -35,7 +35,7 @@ import org.springframework.security.providers.UsernamePasswordAuthenticationToke
import org.springframework.security.ui.AuthenticationDetailsSource;
import org.springframework.security.ui.AuthenticationDetailsSourceImpl;
import org.springframework.security.ui.SpringSecurityFilter;
import org.springframework.security.ui.FilterChainOrderUtils;
import org.springframework.security.ui.FilterChainOrder;
import org.springframework.security.ui.AbstractProcessingFilter;
import org.springframework.security.userdetails.UserDetails;
@ -482,6 +482,6 @@ public class SwitchUserProcessingFilter extends SpringSecurityFilter implements
}
public int getOrder() {
return FilterChainOrderUtils.SWITCH_USER_FILTER_ORDER;
return FilterChainOrder.SWITCH_USER_FILTER;
}
}

View File

@ -21,7 +21,7 @@ import org.springframework.security.AuthenticationException;
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
import org.springframework.security.ui.AbstractProcessingFilter;
import org.springframework.security.ui.FilterChainOrderUtils;
import org.springframework.security.ui.FilterChainOrder;
import org.springframework.util.Assert;
import javax.servlet.http.HttpServletRequest;
@ -154,7 +154,7 @@ public class AuthenticationProcessingFilter extends AbstractProcessingFilter {
}
public int getOrder() {
return FilterChainOrderUtils.AUTH_PROCESSING_FILTER_ORDER;
return FilterChainOrder.AUTH_PROCESSING_FILTER;
}
String getUsernameParameter() {

View File

@ -10,7 +10,7 @@ import javax.servlet.http.HttpSession;
import org.springframework.security.AuthenticationException;
import org.springframework.security.ui.AbstractProcessingFilter;
import org.springframework.security.ui.FilterChainOrderUtils;
import org.springframework.security.ui.FilterChainOrder;
import org.springframework.security.ui.SpringSecurityFilter;
import org.springframework.security.ui.rememberme.AbstractRememberMeServices;
@ -89,7 +89,7 @@ public class DefaultLoginPageGeneratingFilter extends SpringSecurityFilter {
}
public int getOrder() {
return FilterChainOrderUtils.LOGIN_PAGE_FILTER_ORDER;
return FilterChainOrder.LOGIN_PAGE_FILTER;
}
private boolean isLoginUrlRequest(HttpServletRequest request) {

View File

@ -23,7 +23,7 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.ui.FilterChainOrderUtils;
import org.springframework.security.ui.FilterChainOrder;
import org.springframework.security.ui.SpringSecurityFilter;
import org.springframework.security.util.PortResolver;
import org.springframework.security.util.PortResolverImpl;
@ -92,6 +92,6 @@ public class SecurityContextHolderAwareRequestFilter extends SpringSecurityFilte
}
public int getOrder() {
return FilterChainOrderUtils.SECURITY_CONTEXT_HOLDER_AWARE_FILTER_ORDER;
return FilterChainOrder.SECURITY_CONTEXT_HOLDER_AWARE_FILTER;
}
}

View File

@ -258,3 +258,12 @@ jdbc-user-service =
jdbc-user-service.attlist &=
## The bean ID of the DataSource which provides the required tables.
attribute data-source {xsd:string}
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}

View File

@ -629,4 +629,19 @@
</xs:annotation>
</xs:attribute>
</xs:attributeGroup>
<xs:element name="user-filter">
<xs:annotation>
<xs:documentation>Used to indicate that a filter bean declaration should be incorporated into the security filter chain. </xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:attributeGroup ref="security:user-filter.attlist"/>
</xs:complexType>
</xs:element>
<xs:attributeGroup name="user-filter.attlist">
<xs:attribute name="order" use="required" type="xs:integer">
<xs:annotation>
<xs:documentation>The order value for the chain (user to position the filter relative to the standard filters) </xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:attributeGroup>
</xs:schema>

View File

@ -12,6 +12,7 @@ import org.springframework.security.ui.webapp.AuthenticationProcessingFilter;
import org.springframework.security.ui.webapp.DefaultLoginPageGeneratingFilter;
import org.springframework.security.util.FilterChainProxy;
import org.springframework.security.util.PortMapperImpl;
import org.springframework.security.util.MockFilter;
import org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.beans.BeansException;
@ -65,7 +66,7 @@ public class HttpSecurityBeanDefinitionParserTests {
List filterList = filterChainProxy.getFilters("/someurl");
assertEquals("Expected 11 filters in chain", 11, filterList.size());
assertEquals("Expected 12 filters in chain", 12, filterList.size());
Iterator filters = filterList.iterator();
@ -80,6 +81,7 @@ public class HttpSecurityBeanDefinitionParserTests {
assertTrue(filters.next() instanceof RememberMeProcessingFilter);
assertTrue(filters.next() instanceof ExceptionTranslationFilter);
assertTrue(filters.next() instanceof FilterSecurityInterceptor);
assertTrue(filters.next() instanceof OrderedFilterDecorator);
}

View File

@ -40,4 +40,13 @@ http://www.springframework.org/schema/security http://www.springframework.org/sc
<!-- bean name="rememberMeServices" class="org.springframework.security.ui.rememberme.NullRememberMeServices"/ -->
<beans:bean id="userFilter" class="org.springframework.security.util.MockFilter">
<user-filter order="0"/>
</beans:bean>
<!-- Shouldn't be added to filter chain -->
<beans:bean id="userFilter2" class="org.springframework.security.util.MockFilter"/>
</beans:beans>

View File

@ -26,7 +26,7 @@ import org.springframework.security.providers.UsernamePasswordAuthenticationToke
import org.springframework.security.providers.anonymous.AnonymousAuthenticationToken;
import org.springframework.security.ui.SpringSecurityFilter;
import org.springframework.security.ui.WebAuthenticationDetails;
import org.springframework.security.ui.FilterChainOrderUtils;
import org.springframework.security.ui.FilterChainOrder;
import org.springframework.security.ui.webapp.AuthenticationProcessingFilter;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
@ -497,6 +497,6 @@ public class NtlmProcessingFilter extends SpringSecurityFilter implements Initia
}
public int getOrder() {
return FilterChainOrderUtils.NTLM_FILTER_ORDER;
return FilterChainOrder.NTLM_FILTER;
}
}