@ -15,37 +15,37 @@
@@ -15,37 +15,37 @@
package org.springframework.security.web ;
import java.io.IOException ;
import java.util.Collection ;
import java.util.Iterator ;
import java.util.LinkedHashMap ;
import java.util.LinkedHashSet ;
import java.util.List ;
import java.util.Map ;
import java.util.Set ;
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 org.apache.commons.logging.Log ;
import org.apache.commons.logging.LogFactory ;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource ;
import org.springframework.security.web.firewall.DefaultHttpFirewall ;
import org.springframework.security.web.firewall.FirewalledRequest ;
import org.springframework.security.web.firewall.HttpFirewall ;
import org.springframework.security.web.util.AntUrlPathMatcher ;
import org.springframework.security.web.util.UrlMatcher ;
import org.springframework.security.web.util.UrlUtils ;
import org.springframework.util.Assert ;
import org.springframework.web.filter.DelegatingFilterProxy ;
import org.springframework.web.filter.GenericFilterBean ;
import javax.servlet.Filter ;
import javax.servlet.FilterChain ;
import javax.servlet.FilterConfig ;
import javax.servlet.ServletException ;
import javax.servlet.ServletRequest ;
import javax.servlet.ServletRequestWrapper ;
import javax.servlet.ServletResponse ;
import javax.servlet.http.HttpServletRequest ;
import javax.servlet.http.HttpServletResponse ;
import java.io.IOException ;
import java.util.* ;
/ * *
* 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
* Delegates { @code Filter } requests to a list of Spring - managed filter beans .
* As of version 2 . 0 , you shouldn ' t need to explicitly configure a { @code FilterChainProxy } 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 > & lt ; security : http / & gt < / tt > namespace configuration options .
* by the default < tt > & lt ; security : http / & gt ; < / tt > namespace configuration options .
*
* < p > The < code > FilterChainProxy < / code > is loaded via a standard Spring { @link DelegatingFilterProxy } declaration in
* < code > web . xml < / code > . < code > FilterChainProxy < / code > will then pass { @link # init ( FilterConfig ) } , { @link # destroy ( ) }
@ -71,7 +71,7 @@ import org.springframework.web.filter.GenericFilterBean;
@@ -71,7 +71,7 @@ import org.springframework.web.filter.GenericFilterBean;
& lt ; / bean >
* < / pre >
*
* The names "filter1" , "filter2" , "filter3" should be the bean names of < tt > Filter < / tt > instances defined in the
* The names "filter1" , "filter2" , "filter3" should be the bean names of { @code Filter } 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 .
@ -86,17 +86,32 @@ import org.springframework.web.filter.GenericFilterBean;
@@ -86,17 +86,32 @@ import org.springframework.web.filter.GenericFilterBean;
*
* < 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 original or < code > FilterChainProxy < / code > - declared filter
* javax . servlet . FilterChain ) } , in that the remainder of the original or { @code FilterChainProxy } - declared filter
* chain will not be called .
*
* < p > Note the < code > Filter < / code > lifecycle mismatch between the servlet container and IoC
* container . As described in the { @link DelegatingFilterProxy } JavaDocs , we recommend you allow the IoC
* container to manage the lifecycle instead of the servlet container . By default the < code > DelegatingFilterProxy < / code >
* will never call this class ' { @link # init ( FilterConfig ) } and { @link # destroy ( ) } methods , which in turns means that
* the corresponding methods on the filter beans managed by this class will never be called . If you do need your filters to be
* initialized and destroyed , please set the < tt > targetFilterLifecycle < / tt > initialization parameter against the
* < code > DelegatingFilterProxy < / code > to specify that servlet container lifecycle management should be used . You don ' t
* need to worry about this in most cases .
* < h3 > Request Firewalling < / h3 >
*
* An { @link HttpFirewall } instance is used to validate incoming requests and create a wrapped request which provides
* consistent path values for matching against . See { @link DefaultHttpFirewall } , for more information on the type of
* attacks which the default implementation protects against . A custom implementation can be injected to provide
* stricter control over the request contents or if an application needs to support certain types of request which
* are rejected by default .
* < p >
* Note that this means that you must use the Spring Security filters in combination with a { @code FilterChainProxy }
* if you want this protection . Don ' t define them explicitly in your { @code web . xml } file .
* < p >
* { @code FilterChainProxy } will use the firewall instance to obtain both request and response objects which will be
* fed down the filter chain , so it is also possible to use this functionality to control the functionality of the
* response . When the request has passed through the security filter chain , the { @code reset } method will be called .
* With the default implementation this means that the original values of { @code servletPath } and { @code pathInfo } will
* be returned thereafter , instead of the modified ones used for security pattern matching .
*
* < h2 > Filter Lifecycle < / h2 >
* < p >
* Note the { @code Filter } lifecycle mismatch between the servlet container and IoC
* container . As described in the { @link DelegatingFilterProxy } Javadocs , we recommend you allow the IoC
* container to manage the lifecycle instead of the servlet container . { @code FilterChainProxy } does not invoke the
* standard filter lifecycle methods on any filter beans that you add to the application context .
*
* @author Carlos Sanchez
* @author Ben Alex
@ -118,6 +133,7 @@ public class FilterChainProxy extends GenericFilterBean {
@@ -118,6 +133,7 @@ public class FilterChainProxy extends GenericFilterBean {
private Map < Object , List < Filter > > filterChainMap ;
private UrlMatcher matcher = new AntUrlPathMatcher ( ) ;
private boolean stripQueryStringFromUrls = true ;
private HttpFirewall firewall = new DefaultHttpFirewall ( ) ;
private FilterChainValidator filterChainValidator = new NullFilterChainValidator ( ) ;
//~ Methods ========================================================================================================
@ -131,22 +147,24 @@ public class FilterChainProxy extends GenericFilterBean {
@@ -131,22 +147,24 @@ public class FilterChainProxy extends GenericFilterBean {
public void doFilter ( ServletRequest request , ServletResponse response , FilterChain chain )
throws IOException , ServletException {
FilterInvocation fi = new FilterInvocation ( request , response , chain ) ;
List < Filter > filters = getFilters ( fi . getRequestUrl ( ) ) ;
FirewalledRequest fwRequest = firewall . getFirewalledRequest ( ( HttpServletRequest ) request ) ;
HttpServletResponse fwResponse = firewall . getFirewalledResponse ( ( HttpServletResponse ) response ) ;
String url = UrlUtils . buildRequestUrl ( fwRequest ) ;
List < Filter > filters = getFilters ( url ) ;
if ( filters = = null | | filters . size ( ) = = 0 ) {
if ( logger . isDebugEnabled ( ) ) {
logger . debug ( fi . getRequestUrl ( ) +
filters = = null ? " has no matching filters" : " has an empty filter list" ) ;
logger . debug ( url + ( filters = = null ? " has no matching filters" : " has an empty filter list" ) ) ;
}
chain . doFilter ( request , r esponse) ;
chain . doFilter ( fwRequest , fwR esponse) ;
return ;
}
VirtualFilterChain virtualFilterChain = new VirtualFilterChain ( fi , filters ) ;
virtualFilterChain . doFilter ( fi . getRequest ( ) , fi . getResponse ( ) ) ;
VirtualFilterChain vfc = new VirtualFilterChain ( url , chain , filters ) ;
vfc . doFilter ( fwRequest , fwResponse ) ;
}
/ * *
@ -213,14 +231,14 @@ public class FilterChainProxy extends GenericFilterBean {
@@ -213,14 +231,14 @@ public class FilterChainProxy extends GenericFilterBean {
/ * *
* Sets the mapping of URL patterns to filter chains .
*
* The map keys should be the paths and the values should be arrays of < tt > Filter < / tt > objects .
* The map keys should be the paths and the values should be arrays of { @code Filter } objects .
* It ' s VERY important that the type of map used preserves ordering - the order in which the iterator
* returns the entries must be the same as the order they were added to the map , otherwise you have no way
* of guaranteeing that the most specific patterns are returned before the more general ones . So make sure
* the Map used is an instance of < tt > LinkedHashMap < / tt > or an equivalent , rather than a plain < tt > HashMap < / tt > , for
* the Map used is an instance of { @code LinkedHashMap } or an equivalent , rather than a plain { @code HashMap } , for
* example .
*
* @param filterChainMap the map of path Strings to < tt > List & lt ; Filter & gt ; < / tt > s .
* @param filterChainMap the map of path Strings to { @code List & lt ; Filter & gt ; } s .
* /
@SuppressWarnings ( "unchecked" )
public void setFilterChainMap ( Map filterChainMap ) {
@ -270,7 +288,7 @@ public class FilterChainProxy extends GenericFilterBean {
@@ -270,7 +288,7 @@ public class FilterChainProxy extends GenericFilterBean {
/ * *
* Returns a copy of the underlying filter chain map . Modifications to the map contents
* will not affect the FilterChainProxy state - to change the map call < tt > setFilterChainMap < / tt > .
* will not affect the FilterChainProxy state - to change the map call { @code setFilterChainMap } .
*
* @return the map of path pattern Strings to filter chain lists ( with ordering guaranteed ) .
* /
@ -317,42 +335,53 @@ public class FilterChainProxy extends GenericFilterBean {
@@ -317,42 +335,53 @@ public class FilterChainProxy extends GenericFilterBean {
//~ Inner Classes ==================================================================================================
/ * *
* A < code > FilterChain < / code > that records whether or not { @link
* FilterChain # doFilter ( javax . servlet . ServletRequest , javax . servlet . ServletResponse ) } is called .
* < p >
* This < code > FilterChain < / code > is used by < code > FilterChainProxy < / code > to determine if the next
* < code > Filter < / code > should be called or not . < / p >
* Internal { @code FilterChain } implementation that is used to pass a request through the additional
* internal list of filters which match the request .
* /
private static class VirtualFilterChain implements FilterChain {
private FilterInvocation fi ;
private List < Filter > additionalFilters ;
private final FilterChain originalChain ;
private final List < Filter > additionalFilters ;
private final String url ;
private int currentPosition = 0 ;
private VirtualFilterChain ( FilterInvocation filterInvocation , List < Filter > additionalFilters ) {
this . fi = filterInvocation ;
private VirtualFilterChain ( String url , FilterChain chain , List < Filter > additionalFilters ) {
this . originalChain = chain ;
this . url = url ;
this . additionalFilters = additionalFilters ;
}
public void doFilter ( ServletRequest request , ServletResponse response ) throws IOException , ServletException {
public void doFilter ( final ServletRequest request , final ServletResponse response ) throws IOException , ServletException {
if ( currentPosition = = additionalFilters . size ( ) ) {
if ( logger . isDebugEnabled ( ) ) {
logger . debug ( fi . getRequestUrl ( )
+ " reached end of additional filter chain; proceeding with original chain" ) ;
logger . debug ( url + " reached end of additional filter chain; proceeding with original chain" ) ;
}
fi . getChain ( ) . doFilter ( request , response ) ;
// Deactivate path stripping as we exit the security filter chain
resetWrapper ( request ) ;
originalChain . doFilter ( request , response ) ;
} else {
currentPosition + + ;
Filter nextFilter = additionalFilters . get ( currentPosition - 1 ) ;
if ( logger . isDebugEnabled ( ) ) {
logger . debug ( fi . getReq uestU rl( ) + " at position " + currentPosition + " of "
logger . debug ( url + " at position " + currentPosition + " of "
+ additionalFilters . size ( ) + " in additional filter chain; firing Filter: '"
+ nextFilter + "'" ) ;
+ nextFilter . getClass ( ) . getSimpleName ( ) + "'" ) ;
}
nextFilter . doFilter ( request , response , this ) ;
nextFilter . doFilter ( request , response , this ) ;
}
}
private void resetWrapper ( ServletRequest request ) {
while ( request instanceof ServletRequestWrapper ) {
if ( request instanceof FirewalledRequest ) {
( ( FirewalledRequest ) request ) . reset ( ) ;
break ;
}
request = ( ( ServletRequestWrapper ) request ) . getRequest ( ) ;
}
}
}