SEC-1584: Doc updates to explain request matching process.

This commit is contained in:
Luke Taylor 2010-10-03 22:43:19 +01:00
parent dc1b652512
commit 161710cc87
2 changed files with 200 additions and 74 deletions

View File

@ -129,7 +129,11 @@
<literal>&lt;http></literal> element is the parent for all web-related namespace <literal>&lt;http></literal> element is the parent for all web-related namespace
functionality. The <literal>&lt;intercept-url></literal> element defines a functionality. The <literal>&lt;intercept-url></literal> element defines a
<literal>pattern</literal> which is matched against the URLs of incoming requests using an <literal>pattern</literal> which is matched against the URLs of incoming requests using an
ant path style syntax. The <literal>access</literal> attribute defines the access ant path style syntax<footnote>
<para>See the section on <link xlink:href="#request-matching">Request
Matching</link> in the Web Application Infrastructure chapter for more details
on how matches are actually performed.</para>
</footnote>. The <literal>access</literal> attribute defines the access
requirements for requests matching the given pattern. With the default configuration, this requirements for requests matching the given pattern. With the default configuration, this
is typically a comma-separated list of roles, one of which a user must have to be allowed to is typically a comma-separated list of roles, one of which a user must have to be allowed to
make the request. The prefix <quote>ROLE_</quote> is a marker which indicates that a simple make the request. The prefix <quote>ROLE_</quote> is a marker which indicates that a simple

View File

@ -40,29 +40,32 @@
<url-pattern>/*</url-pattern> <url-pattern>/*</url-pattern>
</filter-mapping>]]> </filter-mapping>]]>
</programlisting> Notice that the filter is actually a </programlisting> Notice that the filter is actually a
<literal>DelegatingFilterProxy</literal>, and not the class that will actually implement <literal>DelegatingFilterProxy</literal>, and not the class that will actually
the logic of the filter. What <classname>DelegatingFilterProxy</classname> does is delegate implement the logic of the filter. What <classname>DelegatingFilterProxy</classname>
the <interfacename>Filter</interfacename>'s methods through to a bean which is obtained from does is delegate the <interfacename>Filter</interfacename>'s methods through to a
the Spring application context. This enables the bean to benefit from the Spring web bean which is obtained from the Spring application context. This enables the bean to
application context lifecycle support and configuration flexibility. The bean must implement benefit from the Spring web application context lifecycle support and configuration
<interfacename>javax.servlet.Filter</interfacename> and it must have the same name as that flexibility. The bean must implement
in the <literal>filter-name</literal> element. Read the Javadoc for <interfacename>javax.servlet.Filter</interfacename> and it must have the same name
<classname>DelegatingFilterProxy</classname> for more information</para> as that in the <literal>filter-name</literal> element. Read the Javadoc for
</section> <classname>DelegatingFilterProxy</classname> for more information</para>
<section xml:id="filter-chain-proxy"> </section>
<title><classname>FilterChainProxy</classname></title> <section xml:id="filter-chain-proxy">
<para> It should now be clear that you can declare each Spring Security filter bean that you <title><classname>FilterChainProxy</classname></title>
require in your application context file and add a corresponding <para>Spring Security's web infrastructure should only be used by delegating to an
<classname>DelegatingFilterProxy</classname> entry to <filename>web.xml</filename> for instance of <classname>FilterChainProxy</classname>. The security filters should not
each filter, making sure that they are ordered correctly. This is a cumbersome approach and be used by themselves In theory you could declare each Spring Security filter bean
clutters up the <filename>web.xml</filename> file quickly if we have a lot of filters. We that you require in your application context file and add a corresponding
would prefer to just add a single entry to <filename>web.xml</filename> and deal entirely <classname>DelegatingFilterProxy</classname> entry to <filename>web.xml</filename>
with the application context file for managing our web security beans. This is where Spring for each filter, making sure that they are ordered correctly, but this would be
Secuiryt's <classname>FilterChainProxy</classname> comes in. It is wired using a cumbersome and would clutter up the <filename>web.xml</filename> file quickly if you
<literal>DelegatingFilterProxy</literal>, just like in the example above, but with the have a lot of filters. <classname>FilterChainProxy</classname> lets us add a single
<literal>filter-name</literal> set to the bean name <quote>filterChainProxy</quote>. The entry to <filename>web.xml</filename> and deal entirely with the application context
filter chain is then declared in the application context with the same bean name. Here's an file for managing our web security beans. It is wired using a
example: <programlisting language="xml"><![CDATA[ <literal>DelegatingFilterProxy</literal>, just like in the example above, but with
the <literal>filter-name</literal> set to the bean name
<quote>filterChainProxy</quote>. The filter chain is then declared in the
application context with the same bean name. Here's an example: <programlisting language="xml"><![CDATA[
<bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy"> <bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy">
<sec:filter-chain-map path-type="ant"> <sec:filter-chain-map path-type="ant">
<sec:filter-chain pattern="/webServices/**" filters=" <sec:filter-chain pattern="/webServices/**" filters="
@ -78,56 +81,175 @@
</sec:filter-chain-map> </sec:filter-chain-map>
</bean> </bean>
]]> ]]>
</programlisting> The namespace element <literal>filter-chain-map</literal> is </programlisting> The namespace element <literal>filter-chain-map</literal> is used to set
used to set up the security filter chain(s) which are required within the application<footnote> up the security filter chain(s) which are required within the application<footnote>
<para>Note that you'll need to include the security namespace in your application context <para>Note that you'll need to include the security namespace in your application
XML file in order to use this syntax.</para> context XML file in order to use this syntax.</para>
</footnote>. It maps a particular URL pattern to a chain of filters built up from the bean </footnote>. It maps a particular URL pattern to a chain of filters built up from
names specified in the <literal>filters</literal> element. Both regular expressions and Ant the bean names specified in the <literal>filters</literal> element. Both regular
Paths are supported, and the most specific URIs appear first. At runtime the expressions and Ant Paths are supported, and the most specific URLs appear first. At
<classname>FilterChainProxy</classname> will locate the first URI pattern that matches the runtime the <classname>FilterChainProxy</classname> will locate the first URL
current web request and the list of filter beans specified by the <literal>filters</literal> pattern that matches the current web request and the list of filter beans specified
attribute will be applied to that request. The filters will be invoked in the order they are by the <literal>filters</literal> attribute will be applied to that request. The
defined, so you have complete control over the filter chain which is applied to a particular filters will be invoked in the order they are defined, so you have complete control
URL.</para> over the filter chain which is applied to a particular URL.</para>
<para>You may have noticed we have declared two <para>You may have noticed we have declared two
<classname>SecurityContextPersistenceFilter</classname>s in the filter chain <classname>SecurityContextPersistenceFilter</classname>s in the filter chain
(<literal>ASC</literal> is short for <literal>allowSessionCreation</literal>, a property (<literal>ASC</literal> is short for <literal>allowSessionCreation</literal>, a
of <classname>SecurityContextPersistenceFilter</classname>). As web services will never property of <classname>SecurityContextPersistenceFilter</classname>). As web
present a <literal>jsessionid</literal> on future requests, creating services will never present a <literal>jsessionid</literal> on future requests,
<literal>HttpSession</literal>s for such user agents would be wasteful. If you had a creating <literal>HttpSession</literal>s for such user agents would be wasteful. If
high-volume application which required maximum scalability, we recommend you use the you had a high-volume application which required maximum scalability, we recommend
approach shown above. For smaller applications, using a single you use the approach shown above. For smaller applications, using a single
<classname>SecurityContextPersistenceFilter</classname> (with its default <classname>SecurityContextPersistenceFilter</classname> (with its default
<literal>allowSessionCreation</literal> as <literal>true</literal>) would likely be <literal>allowSessionCreation</literal> as <literal>true</literal>) would likely be
sufficient.</para> sufficient.</para>
<para>In relation to lifecycle issues, the <classname>FilterChainProxy</classname> will always <para>Note that <classname>FilterChainProxy</classname> does not invoke standard filter
delegate <methodname>init(FilterConfig)</methodname> and <methodname>destroy()</methodname> lifecycle methods on the filters it is configured with. We recommend you use
methods through to the underlaying <interfacename>Filter</interfacename>s if such methods Spring's application context lifecycle interfaces as an alternative, just as you
are called against <classname>FilterChainProxy</classname> itself. In this case, would for any other Spring bean.</para>
<classname>FilterChainProxy</classname> guarantees to only initialize and destroy each <para> When we looked at how to set up web security using <link
<literal>Filter</literal> bean once, no matter how many times it is declared in the filter xlink:href="#namespace-auto-config">namespace configuration</link>, we used a
chain(s). You control the overall choice as to whether these methods are called or not via <literal>DelegatingFilterProxy</literal> with the name
the <literal>targetFilterLifecycle</literal> initialization parameter of <quote>springSecurityFilterChain</quote>. You should now be able to see that this is
<literal>DelegatingFilterProxy</literal>. By default this property is the name of the <classname>FilterChainProxy</classname> which is created by the
<literal>false</literal> and servlet container lifecycle invocations are not delegated namespace. </para>
through <literal>DelegatingFilterProxy</literal>.</para> <section>
<para> When we looked at how to set up web security using <link <title>Bypassing the Filter Chain</title>
xlink:href="#namespace-auto-config">namespace configuration</link>, we used a <para>As with the namespace, you can use the attribute <literal>filters = "none"</literal> as an
<literal>DelegatingFilterProxy</literal> with the name alternative to supplying a filter bean list. This will omit the request pattern
<quote>springSecurityFilterChain</quote>. You should now be able to see that this is the from the security filter chain entirely. Note that anything matching this path
name of the <classname>FilterChainProxy</classname> which is created by the namespace. </para> will then have no authentication or authorization services applied and will be
<section> freely accessible. If you want to make use of the contents of the
<title>Bypassing the Filter Chain</title> <classname>SecurityContext</classname> contents during a request, then it must
<para> As with the namespace, you can use the attribute <literal>filters = "none"</literal> have passed through the security filter chain. Otherwise the
as an alternative to supplying a filter bean list. This will omit the request pattern from <classname>SecurityContextHolder</classname> will not have been populated and
the security filter chain entirely. Note that anything matching this path will then have the contents will be null.</para>
no authentication or authorization services applied and will be freely accessible. If you </section>
want to make use of the contents of the <classname>SecurityContext</classname> contents </section>
during a request, then it must have passed through the security filter chain. Otherwise <section>
the <classname>SecurityContextHolder</classname> will not have been populated and the <title>Filter Ordering</title>
contents will be null.</para> <para>The order that filters are defined in the chain is very important. Irrespective of
</section> which filters you are actually using, the order should be as follows: <orderedlist>
<listitem>
<para><classname>ChannelProcessingFilter</classname>, because it might need to
redirect to a different protocol</para>
</listitem>
<listitem>
<para><classname>ConcurrentSessionFilter</classname>, because it doesn't use any
<classname>SecurityContextHolder</classname> functionality but needs to
update the <interfacename>SessionRegistry</interfacename> to reflect ongoing
requests from the principal</para>
</listitem>
<listitem>
<para><classname>SecurityContextPersistenceFilter</classname>, so a
<interfacename>SecurityContext</interfacename> can be set up in the
<classname>SecurityContextHolder</classname> at the beginning of a web
request, and any changes to the
<interfacename>SecurityContext</interfacename> can be copied to the
<literal>HttpSession</literal> when the web request ends (ready for use with
the next web request)</para>
</listitem>
<listitem>
<para>Authentication processing mechanisms -
<classname>UsernamePasswordAuthenticationFilter</classname>,
<classname>CasProcessingFilter</classname>,
<classname>BasicProcessingFilter</classname> etc - so that the
<classname>SecurityContextHolder</classname> can be modified to contain a
valid <interfacename>Authentication</interfacename> request token</para>
</listitem>
<listitem>
<para>The <literal>SecurityContextHolderAwareRequestFilter</literal>, if you are
using it to install a Spring Security aware
<literal>HttpServletRequestWrapper</literal> into your servlet
container</para>
</listitem>
<listitem>
<para><classname>RememberMeProcessingFilter</classname>, so that if no earlier
authentication processing mechanism updated the
<classname>SecurityContextHolder</classname>, and the request presents a
cookie that enables remember-me services to take place, a suitable
remembered <interfacename>Authentication</interfacename> object will be put
there</para>
</listitem>
<listitem>
<para><classname>AnonymousProcessingFilter</classname>, so that if no earlier
authentication processing mechanism updated the
<classname>SecurityContextHolder</classname>, an anonymous
<interfacename>Authentication</interfacename> object will be put
there</para>
</listitem>
<listitem>
<para><classname>ExceptionTranslationFilter</classname>, to catch any Spring
Security exceptions so that either an HTTP error response can be returned or
an appropriate <interfacename>AuthenticationEntryPoint</interfacename> can
be launched</para>
</listitem>
<listitem>
<para><classname>FilterSecurityInterceptor</classname>, to protect web URIs and
raise exceptions when access is denied</para>
</listitem>
</orderedlist></para>
</section>
<section xml:id="request-matching">
<title>Request Matching</title>
<para>Spring Security has several areas where patterns you have defined are tested
against incoming requests in order to decide how the request should be handled. This
occurs when the <classname>FilterChainProxy</classname> decides which filter chain a
request should be passed through and also when the
<classname>FilterSecurityInterceptor</classname> decides which security constraints
apply to a request. It's important to understand what the mechanism is and what URL
value is used when testing against the patterns that you define.</para>
<para>The Servlet Specification defines several properties for the
<interfacename>HttpServletRequest</interfacename> which are accessible via getter
methods, and which we might want to match against. These are the
<literal>contextPath</literal>, <literal>servletPath</literal>,
<literal>pathInfo</literal> and <literal>queryString</literal>. Spring Security is
only interested in securing paths within the application, so the
<literal>contextPath</literal> is ignored. Each path segment of a URL may contain
parameters, as defined in <link xlink:href="http://www.ietf.org/rfc/rfc2396.txt">RFC
2396</link><footnote>
<para>You have probably seen this when a browser doesn't support cookies and the
<literal>jsessionid</literal> parameter is appended to the URL after a
semi-colon. However the RFC allows the presence of these parameters in any path
segment of the URL</para>
</footnote>. The Specification does not clearly state whether these should be
included in the <literal>servletPath</literal> and <literal>pathInfo</literal> value
and the behaviour varies between different servlet containers. There is a danger
that when an application is deployed in a container which does not strip path
parameters from these values, an attacker could add them to the requested URL in
order to cause a pattern match to succeed or fail unexpectedly. Spring Security's
<classname>FilterChainProxy</classname> therefore wraps incoming requests to
consistently return <literal>servletPath</literal> and <literal>pathInfo</literal>
values which do not contain path parameters. For example, an original request path
<literal>/secure;hack=1/somefile.html;hack=2</literal> will be returned as
<literal>/secure/somefile.html</literal>. It is therefore essential that a
<classname>FilterChainProxy</classname> is used to manage the security filter
chain.</para>
<para>As mentioned above, the default strategy is to use Ant-style paths for matching
and this is likely to be the best choice for most users. Matching is performed
a pattern against the concatenated <literal>servletPath</literal> and
<literal>pathInfo</literal>, ignoring the <literal>queryString</literal>, and is case insensitive by default.</para>
<para>In practice we recommend that you use method security at your service layer, to
control access to your application, and do not rely entirely on the use of security
constraints defined at the web-application level. URLs change and it is difficult to
take account of all the possible URLs that an application might support and how
requests might be manipulated. You should try and restrict yourself to using a few
simple ant paths which are simple to understand. Always try to use a
<quote>deny-by-default</quote> approach where you have a catch-all wildcard
(<literal>/**</literal>) defined last and denying access.</para>
<para>Security defined at the service layer is much more robust and harder to bypass, so
you should always take advantage of Spring Security's method security
options.</para>
</section>
<section>
<title>Use with other Filter-Based Frameworks</title>
<para>If you're using some other framework that is also filter-based, then you need to
make sure that the Spring Security filters come first. This enables the
<classname>SecurityContextHolder</classname> to be populated in time for use by the
other filters. Examples are the use of SiteMesh to decorate your web pages or a web
framework like Wicket which uses a filter to handle its requests. </para>
</section>
</section> </section>
<section> <section>
<title>Filter Ordering</title> <title>Filter Ordering</title>