|
@@ -15,11 +15,10 @@
|
|
|
|
|
|
package org.springframework.security.util;
|
|
|
|
|
|
-import org.springframework.security.ConfigAttribute;
|
|
|
-import org.springframework.security.ConfigAttributeDefinition;
|
|
|
-
|
|
|
import org.springframework.security.intercept.web.FilterInvocation;
|
|
|
import org.springframework.security.intercept.web.FilterInvocationDefinitionSource;
|
|
|
+import org.springframework.security.intercept.web.FilterChainMap;
|
|
|
+import org.springframework.security.intercept.web.FIDSToFilterChainMapConverter;
|
|
|
|
|
|
import org.apache.commons.logging.Log;
|
|
|
import org.apache.commons.logging.LogFactory;
|
|
@@ -34,12 +33,6 @@ import org.springframework.util.Assert;
|
|
|
|
|
|
import java.io.IOException;
|
|
|
|
|
|
-import java.util.Iterator;
|
|
|
-import java.util.LinkedHashSet;
|
|
|
-import java.util.List;
|
|
|
-import java.util.Set;
|
|
|
-import java.util.Vector;
|
|
|
-
|
|
|
import javax.servlet.Filter;
|
|
|
import javax.servlet.FilterChain;
|
|
|
import javax.servlet.FilterConfig;
|
|
@@ -49,22 +42,48 @@ import javax.servlet.ServletResponse;
|
|
|
|
|
|
|
|
|
/**
|
|
|
- * Delegates <code>Filter</code> requests to a list of Spring-managed beans.<p>The <code>FilterChainProxy</code> is
|
|
|
- * loaded via a standard {@link org.springframework.security.util.FilterToBeanProxy} declaration in <code>web.xml</code>.
|
|
|
+ * Delegates <code>Filter</code> requests to a list of Spring-managed beans.
|
|
|
+ * As of version 2.0, you shouldn't need to explicitly configure a <tt>FilterChainProxy</tt> bean in your application
|
|
|
+ * context unless you need very fine control over the filter chain contents. Most cases should be adequately covered
|
|
|
+ * by the default <tt><security:http /></tt> namespace configuration options.
|
|
|
+ *
|
|
|
+ * <p>The <code>FilterChainProxy</code> is loaded via a standard
|
|
|
+ * {@link org.springframework.security.util.FilterToBeanProxy} declaration in <code>web.xml</code>.
|
|
|
* <code>FilterChainProxy</code> will then pass {@link #init(FilterConfig)}, {@link #destroy()} and {@link
|
|
|
* #doFilter(ServletRequest, ServletResponse, FilterChain)} invocations through to each <code>Filter</code> defined
|
|
|
* against <code>FilterChainProxy</code>.</p>
|
|
|
- * <p><code>FilterChainProxy</code> is configured using a standard {@link
|
|
|
- * org.springframework.security.intercept.web.FilterInvocationDefinitionSource}. Each possible URI pattern that
|
|
|
- * <code>FilterChainProxy</code> should service must be entered. The first matching URI pattern located by
|
|
|
- * <code>FilterInvocationDefinitionSource</code> for a given request will be used to define all of the
|
|
|
+ *
|
|
|
+ * <p>As of version 2.0, <tt>FilterChainProxy</tt> is configured using a {@link FilterChainMap}. In previous
|
|
|
+ * versions, a {@link FilterInvocationDefinitionSource} was used. This is now deprecated in favour of namespace-based
|
|
|
+ * configuration which provides a more robust and simplfied syntax. The <tt>FilterChainMap</tt> instance will be
|
|
|
+ * created while parsing the namespace configuration, so it doesn't require an explicit bean declaration.
|
|
|
+ * Instead the <filter-chain-map> element should be used within the FilterChainProxy bean declaration.
|
|
|
+ * This in turn should have a list of child <filter-chain> elements which each define a URI pattern and the list
|
|
|
+ * of filters (as comma-separated bean names) which should be applied to requests which match the pattern.
|
|
|
+ * An example configuration might look like this:
|
|
|
+ *
|
|
|
+ * <pre>
|
|
|
+ <bean id="myfilterChainProxy" class="org.springframework.security.util.FilterChainProxy">
|
|
|
+ <security:filter-chain-map pathType="ant">
|
|
|
+ <security:filter-chain pattern="/do/not/filter" filters="none"/>
|
|
|
+ <security:filter-chain pattern="/**" filters="filter1,filter2,filter3"/>
|
|
|
+ </security:filter-chain-map>
|
|
|
+ </bean>
|
|
|
+ * </pre>
|
|
|
+ *
|
|
|
+ * The names "filter1", "filter2", "filter3" should be the bean names of <tt>Filter</tt> instances defined in the
|
|
|
+ * application context. The order of the names defines the order in which the filters will be applied. As shown above,
|
|
|
+ * use of the value "none" for the "filters" can be used to exclude
|
|
|
+ * Please consult the security namespace schema file for a full list of available configuration options.
|
|
|
+ * </p>
|
|
|
+ *
|
|
|
+ *<p>
|
|
|
+ * Each possible URI pattern that <code>FilterChainProxy</code> should service must be entered.
|
|
|
+ * The first matching URI pattern for a given request will be used to define all of the
|
|
|
* <code>Filter</code>s that apply to that request. NB: This means you must put most specific URI patterns at the top
|
|
|
* of the list, and ensure all <code>Filter</code>s that should apply for a given URI pattern are entered against the
|
|
|
* respective entry. The <code>FilterChainProxy</code> will not iterate the remainder of the URI patterns to locate
|
|
|
- * additional <code>Filter</code>s. The <code>FilterInvocationDefinitionSource</code> described the applicable URI
|
|
|
- * pattern to fire the filter chain, followed by a list of configuration attributes. Each configuration attribute's
|
|
|
- * {@link org.springframework.security.ConfigAttribute#getAttribute()} corresponds to a bean name that is available from the
|
|
|
- * application context.</p>
|
|
|
+ * additional <code>Filter</code>s.</p>
|
|
|
* <p><code>FilterChainProxy</code> respects normal handling of <code>Filter</code>s that elect not to call {@link
|
|
|
* javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse,
|
|
|
* javax.servlet.FilterChain)}, in that the remainder of the origial or <code>FilterChainProxy</code>-declared filter
|
|
@@ -73,14 +92,14 @@ import javax.servlet.ServletResponse;
|
|
|
* container. As per {@link org.springframework.security.util.FilterToBeanProxy} JavaDocs, we recommend you allow the IoC
|
|
|
* container to manage lifecycle instead of the servlet container. By default the <code>FilterToBeanProxy</code> will
|
|
|
* never call this class' {@link #init(FilterConfig)} and {@link #destroy()} methods, meaning each of the filters
|
|
|
- * defined against <code>FilterInvocationDefinitionSource</code> will not be called. If you do need your filters to be
|
|
|
+ * defined in the FilterChainMap will not be called. If you do need your filters to be
|
|
|
* initialized and destroyed, please set the <code>lifecycle</code> initialization parameter against the
|
|
|
* <code>FilterToBeanProxy</code> to specify servlet container lifecycle management.</p>
|
|
|
- * <p>If a filter name of {@link #TOKEN_NONE} is used, this allows specification of a filter pattern which should
|
|
|
- * never cause any filters to fire.</p>
|
|
|
*
|
|
|
* @author Carlos Sanchez
|
|
|
* @author Ben Alex
|
|
|
+ * @author Luke Taylor
|
|
|
+ *
|
|
|
* @version $Id$
|
|
|
*/
|
|
|
public class FilterChainProxy implements Filter, InitializingBean, ApplicationContextAware {
|
|
@@ -92,19 +111,22 @@ public class FilterChainProxy implements Filter, InitializingBean, ApplicationCo
|
|
|
//~ Instance fields ================================================================================================
|
|
|
|
|
|
private ApplicationContext applicationContext;
|
|
|
- private FilterInvocationDefinitionSource filterInvocationDefinitionSource;
|
|
|
-
|
|
|
+ private FilterChainMap filterChainMap;
|
|
|
+ private FilterInvocationDefinitionSource fids;
|
|
|
//~ Methods ========================================================================================================
|
|
|
|
|
|
public void afterPropertiesSet() throws Exception {
|
|
|
- Assert.notNull(filterInvocationDefinitionSource, "filterInvocationDefinitionSource must be specified");
|
|
|
- Assert.notNull(this.filterInvocationDefinitionSource.getConfigAttributeDefinitions(),
|
|
|
- "FilterChainProxy requires the FilterInvocationDefinitionSource to return a non-null response to "
|
|
|
- + "getConfigAttributeDefinitions()");
|
|
|
+ // Convert the FilterDefinitionSource to a filterChainMap if set
|
|
|
+ if (fids != null) {
|
|
|
+ Assert.isNull(filterChainMap, "Set the FilterChainMap or FilterInvocationDefinitionSource but not both");
|
|
|
+ setFilterChainMap(new FIDSToFilterChainMapConverter(fids, applicationContext).getFilterChainMap());
|
|
|
+ }
|
|
|
+
|
|
|
+ Assert.notNull(filterChainMap, "A FilterChainMap must be supplied");
|
|
|
}
|
|
|
|
|
|
public void destroy() {
|
|
|
- Filter[] filters = obtainAllDefinedFilters();
|
|
|
+ Filter[] filters = filterChainMap.getAllDefinedFilters();
|
|
|
|
|
|
for (int i = 0; i < filters.length; i++) {
|
|
|
if (filters[i] != null) {
|
|
@@ -118,26 +140,16 @@ public class FilterChainProxy implements Filter, InitializingBean, ApplicationCo
|
|
|
}
|
|
|
|
|
|
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
|
|
- throws IOException, ServletException {
|
|
|
- FilterInvocation fi = new FilterInvocation(request, response, chain);
|
|
|
-
|
|
|
- ConfigAttributeDefinition cad = this.filterInvocationDefinitionSource.getAttributes(fi);
|
|
|
-
|
|
|
- if (cad == null) {
|
|
|
- if (logger.isDebugEnabled()) {
|
|
|
- logger.debug(fi.getRequestUrl() + " has no matching filters");
|
|
|
- }
|
|
|
-
|
|
|
- chain.doFilter(request, response);
|
|
|
+ throws IOException, ServletException {
|
|
|
|
|
|
- return;
|
|
|
- }
|
|
|
+ FilterInvocation fi = new FilterInvocation(request, response, chain);
|
|
|
|
|
|
- Filter[] filters = obtainAllDefinedFilters(cad);
|
|
|
+ Filter[] filters = filterChainMap.getFilters(fi.getRequestUrl());
|
|
|
|
|
|
- if (filters.length == 0) {
|
|
|
+ if (filters == null || filters.length == 0) {
|
|
|
if (logger.isDebugEnabled()) {
|
|
|
- logger.debug(fi.getRequestUrl() + " has an empty filter list");
|
|
|
+ logger.debug(fi.getRequestUrl() +
|
|
|
+ filters == null ? " has no matching filters" : " has an empty filter list");
|
|
|
}
|
|
|
|
|
|
chain.doFilter(request, response);
|
|
@@ -149,12 +161,8 @@ public class FilterChainProxy implements Filter, InitializingBean, ApplicationCo
|
|
|
virtualFilterChain.doFilter(fi.getRequest(), fi.getResponse());
|
|
|
}
|
|
|
|
|
|
- public FilterInvocationDefinitionSource getFilterInvocationDefinitionSource() {
|
|
|
- return filterInvocationDefinitionSource;
|
|
|
- }
|
|
|
-
|
|
|
public void init(FilterConfig filterConfig) throws ServletException {
|
|
|
- Filter[] filters = obtainAllDefinedFilters();
|
|
|
+ Filter[] filters = filterChainMap.getAllDefinedFilters();
|
|
|
|
|
|
for (int i = 0; i < filters.length; i++) {
|
|
|
if (filters[i] != null) {
|
|
@@ -168,70 +176,39 @@ public class FilterChainProxy implements Filter, InitializingBean, ApplicationCo
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Obtains all of the <b>unique</b><code>Filter</code> instances registered against the
|
|
|
- * <code>FilterInvocationDefinitionSource</code>.<p>This is useful in ensuring a <code>Filter</code> is not
|
|
|
+ * Obtains all of the <b>unique</b><code>Filter</code> instances registered in the
|
|
|
+ * <code>FilterChainMap</code>.
|
|
|
+ * <p>This is useful in ensuring a <code>Filter</code> is not
|
|
|
* initialized or destroyed twice.</p>
|
|
|
*
|
|
|
- * @return all of the <code>Filter</code> instances in the application context for which there has been an entry
|
|
|
- * against the <code>FilterInvocationDefinitionSource</code> (only one entry is included in the array for
|
|
|
+ * @deprecated
|
|
|
+ * @return all of the <code>Filter</code> instances in the application context which have an entry
|
|
|
+ * in the <code>FilterChainMap</code> (only one entry is included in the array for
|
|
|
* each <code>Filter</code> that actually exists in application context, even if a given
|
|
|
- * <code>Filter</code> is defined multiples times by the <code>FilterInvocationDefinitionSource</code>)
|
|
|
+ * <code>Filter</code> is defined multiples times by the <code>FilterChainMap</code>)
|
|
|
*/
|
|
|
protected Filter[] obtainAllDefinedFilters() {
|
|
|
- Iterator cads = this.filterInvocationDefinitionSource.getConfigAttributeDefinitions();
|
|
|
- Set list = new LinkedHashSet();
|
|
|
-
|
|
|
- while (cads.hasNext()) {
|
|
|
- ConfigAttributeDefinition attribDef = (ConfigAttributeDefinition) cads.next();
|
|
|
- Filter[] filters = obtainAllDefinedFilters(attribDef);
|
|
|
-
|
|
|
- for (int i = 0; i < filters.length; i++) {
|
|
|
- list.add(filters[i]);
|
|
|
- }
|
|
|
- }
|
|
|
+ return filterChainMap.getAllDefinedFilters();
|
|
|
+ }
|
|
|
|
|
|
- return (Filter[]) list.toArray(new Filter[0]);
|
|
|
+ public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
|
|
+ this.applicationContext = applicationContext;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Obtains all of the <code>Filter</code> instances registered against the specified
|
|
|
- * <code>ConfigAttributeDefinition</code>.
|
|
|
- *
|
|
|
- * @param configAttributeDefinition for which we want to obtain associated <code>Filter</code>s
|
|
|
- *
|
|
|
- * @return the <code>Filter</code>s against the specified <code>ConfigAttributeDefinition</code> (never
|
|
|
- * <code>null</code>)
|
|
|
*
|
|
|
- * @throws IllegalArgumentException DOCUMENT ME!
|
|
|
+ * @deprecated Use namespace configuration or call setFilterChainMap instead.
|
|
|
*/
|
|
|
- private Filter[] obtainAllDefinedFilters(ConfigAttributeDefinition configAttributeDefinition) {
|
|
|
- List list = new Vector();
|
|
|
- Iterator attributes = configAttributeDefinition.getConfigAttributes();
|
|
|
-
|
|
|
- while (attributes.hasNext()) {
|
|
|
- ConfigAttribute attr = (ConfigAttribute) attributes.next();
|
|
|
- String filterName = attr.getAttribute();
|
|
|
-
|
|
|
- if (filterName == null) {
|
|
|
- throw new IllegalArgumentException("Configuration attribute: '" + attr
|
|
|
- + "' returned null to the getAttribute() method, which is invalid when used with FilterChainProxy");
|
|
|
- }
|
|
|
-
|
|
|
- if (!filterName.equals(TOKEN_NONE)) {
|
|
|
- list.add(this.applicationContext.getBean(filterName, Filter.class));
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return (Filter[]) list.toArray(new Filter[list.size()]);
|
|
|
+ public void setFilterInvocationDefinitionSource(FilterInvocationDefinitionSource fids) {
|
|
|
+ this.fids = fids;
|
|
|
}
|
|
|
|
|
|
- public void setApplicationContext(ApplicationContext applicationContext)
|
|
|
- throws BeansException {
|
|
|
- this.applicationContext = applicationContext;
|
|
|
+ public void setFilterChainMap(FilterChainMap filterChainMap) {
|
|
|
+ this.filterChainMap = filterChainMap;
|
|
|
}
|
|
|
|
|
|
- public void setFilterInvocationDefinitionSource(FilterInvocationDefinitionSource filterInvocationDefinitionSource) {
|
|
|
- this.filterInvocationDefinitionSource = filterInvocationDefinitionSource;
|
|
|
+ public FilterChainMap getFilterChainMap() {
|
|
|
+ return filterChainMap;
|
|
|
}
|
|
|
|
|
|
//~ Inner Classes ==================================================================================================
|
|
@@ -242,7 +219,7 @@ public class FilterChainProxy implements Filter, InitializingBean, ApplicationCo
|
|
|
* <code>FilterChain</code> is used by <code>FilterChainProxy</code> to determine if the next <code>Filter</code>
|
|
|
* should be called or not.</p>
|
|
|
*/
|
|
|
- private class VirtualFilterChain implements FilterChain {
|
|
|
+ private static class VirtualFilterChain implements FilterChain {
|
|
|
private FilterInvocation fi;
|
|
|
private Filter[] additionalFilters;
|
|
|
private int currentPosition = 0;
|
|
@@ -252,8 +229,6 @@ public class FilterChainProxy implements Filter, InitializingBean, ApplicationCo
|
|
|
this.additionalFilters = additionalFilters;
|
|
|
}
|
|
|
|
|
|
- private VirtualFilterChain() {}
|
|
|
-
|
|
|
public void doFilter(ServletRequest request, ServletResponse response)
|
|
|
throws IOException, ServletException {
|
|
|
if (currentPosition == additionalFilters.length) {
|
|
@@ -276,4 +251,5 @@ public class FilterChainProxy implements Filter, InitializingBean, ApplicationCo
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
}
|