|
@@ -0,0 +1,307 @@
|
|
|
|
+<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="core-web-filters"
|
|
|
|
+ xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
|
|
+ <title>Core Security Filters</title>
|
|
|
|
+ <para> There are some key filters which will always be used in a web application which uses
|
|
|
|
+ Spring Security, so we'll look at these and their supporting classes and interfaces them
|
|
|
|
+ first. We won't cover every feature, so be sure to look at the Javadoc for them if you want
|
|
|
|
+ to get the complete picture.</para>
|
|
|
|
+ <section xml:id="filter-security-interceptor">
|
|
|
|
+ <title><classname>FilterSecurityInterceptor</classname></title>
|
|
|
|
+ <para>We've already seen <classname>FilterSecurityInterceptor</classname> briefly when
|
|
|
|
+ discussing access-control in general (see<link xlink:href="#tech-intro-access-control"
|
|
|
|
+ />), and we've already used it with the namespace where the
|
|
|
|
+ <literal><intercept-url></literal> elements are combined to configure it
|
|
|
|
+ internally. Now we'll see how to explicitly configure it for use with a
|
|
|
|
+ <classname>FilterChainProxy</classname>, along with its companion filter
|
|
|
|
+ <classname>ExceptionTranslationFilter</classname>. A typical configuration example
|
|
|
|
+ is shown below: <programlisting language="xml"><![CDATA[
|
|
|
|
+<bean id="filterSecurityInterceptor"
|
|
|
|
+ class="org.springframework.security.intercept.web.FilterSecurityInterceptor">
|
|
|
|
+ <property name="authenticationManager" ref="authenticationManager"/>
|
|
|
|
+ <property name="accessDecisionManager" ref="accessDecisionManager"/>
|
|
|
|
+ <property name="securityMetadataSource">
|
|
|
|
+ <security:filter-security-metadata-source>
|
|
|
|
+ <security:intercept-url pattern="/secure/super/**" access="ROLE_WE_DONT_HAVE"/>
|
|
|
|
+ <security:intercept-url pattern="/secure/**" access="ROLE_SUPERVISOR,ROLE_TELLER"/>
|
|
|
|
+ </security:filter-security-metadata-source>
|
|
|
|
+ </property>
|
|
|
|
+</bean>]]></programlisting></para>
|
|
|
|
+ <para><classname>FilterSecurityInterceptor</classname> is responsible for handling the
|
|
|
|
+ security of HTTP resources. It requires a reference to an
|
|
|
|
+ <interfacename>AuthenticationManager</interfacename> and an
|
|
|
|
+ <interfacename>AccessDecisionManager</interfacename>. It is also supplied with
|
|
|
|
+ configuration attributes that apply to different HTTP URL requests. Refer back to <link
|
|
|
|
+ xlink:href="#tech-intro-config-attributes"/> where we saw these originally.</para>
|
|
|
|
+ <para>The <classname>FilterSecurityInterceptor</classname> can be configured with
|
|
|
|
+ configuration attributes in two ways. The first, which is shown above, is using the
|
|
|
|
+ <literal><filter-security-metadata-source></literal> namespace element. This
|
|
|
|
+ is similar to the <literal><filter-chain-map></literal> used to configure a
|
|
|
|
+ <classname>FilterChainProxy</classname> but the
|
|
|
|
+ <literal><intercept-url></literal> child elements only use the
|
|
|
|
+ <literal>pattern</literal> and <literal>access</literal> attributes. Commas are used
|
|
|
|
+ to delimit the different configuration attributes that apply to each HTTP URL. The
|
|
|
|
+ second option is to write your own
|
|
|
|
+ <interfacename>SecurityMetadataSource</interfacename>, but this is beyond the scope of
|
|
|
|
+ this document. Irrespective of the approach used, the
|
|
|
|
+ <interfacename>SecurityMetadataSource</interfacename> is responsible for returning a
|
|
|
|
+ <literal>List<ConfigAttribute></literal> containing all of the configuration
|
|
|
|
+ attributes associated with a single secure HTTP URL.</para>
|
|
|
|
+ <para>It should be noted that the
|
|
|
|
+ <literal>FilterSecurityInterceptor.setSecurityMetadataSource()</literal> method
|
|
|
|
+ actually expects an instance of
|
|
|
|
+ <interfacename>FilterSecurityMetadataSource</interfacename>. This is a marker
|
|
|
|
+ interface which subclasses <interfacename>SecurityMetadataSource</interfacename>. It
|
|
|
|
+ simply denotes the <interfacename>SecurityMetadataSource</interfacename> understands
|
|
|
|
+ <classname>FilterInvocation</classname>s. In the interests of simplicity we'll
|
|
|
|
+ continue to refer to the <interfacename>FilterInvocationDefinitionSource</interfacename>
|
|
|
|
+ as a <interfacename>SecurityMetadataSource</interfacename>, as the distinction is of
|
|
|
|
+ little relevance to most users.</para>
|
|
|
|
+ <para>The <interfacename>SecurityMetadataSource</interfacename> created by the namespace
|
|
|
|
+ syntax obtains the configuration attributes for a particular
|
|
|
|
+ <classname>FilterInvocation</classname> by matching the request URL against the
|
|
|
|
+ configured <literal>pattern</literal> attributes. This behaves in the same way as it
|
|
|
|
+ does for namespace configuration. The default is to treat all expressions as Apache Ant
|
|
|
|
+ paths and regular expressions are also supported for more complex cases. The
|
|
|
|
+ <literal>path-type</literal> attribute is used to specify the type of pattern being
|
|
|
|
+ used. It is not possible to mix expression syntaxes within the same definition. As an
|
|
|
|
+ example, the previous configuration using regular expressions instead of Ant paths would
|
|
|
|
+ be written as follows:</para>
|
|
|
|
+ <programlisting language="xml"><![CDATA[
|
|
|
|
+<bean id="filterInvocationInterceptor"
|
|
|
|
+ class="org.springframework.security.intercept.web.FilterSecurityInterceptor">
|
|
|
|
+ <property name="authenticationManager" ref="authenticationManager"/>
|
|
|
|
+ <property name="accessDecisionManager" ref="accessDecisionManager"/>
|
|
|
|
+ <property name="runAsManager" ref="runAsManager"/>
|
|
|
|
+ <property name="securityMetadataSource">
|
|
|
|
+ <security:filter-security-metadata-source path-type="regex">
|
|
|
|
+ <security:intercept-url pattern="\A/secure/super/.*\Z" access="ROLE_WE_DONT_HAVE"/>
|
|
|
|
+ <security:intercept-url pattern="\A/secure/.*\" access="ROLE_SUPERVISOR,ROLE_TELLER"/>
|
|
|
|
+ </security:filter-security-metadata-source>
|
|
|
|
+ </property>
|
|
|
|
+</bean>]]> </programlisting>
|
|
|
|
+ <para>Patterns are always evaluated in the order they are defined. Thus it is important that
|
|
|
|
+ more specific patterns are defined higher in the list than less specific patterns. This
|
|
|
|
+ is reflected in our example above, where the more specific
|
|
|
|
+ <literal>/secure/super/</literal> pattern appears higher than the less specific
|
|
|
|
+ <literal>/secure/</literal> pattern. If they were reversed, the
|
|
|
|
+ <literal>/secure/</literal> pattern would always match and the
|
|
|
|
+ <literal>/secure/super/</literal> pattern would never be evaluated.</para>
|
|
|
|
+<!--
|
|
|
|
+ TODO: Put in FAQ instead. Or drop.
|
|
|
|
+ <para>As with other security interceptors, the <literal>validateConfigAttributes</literal>
|
|
|
|
+ property is observed. When set to <literal>true</literal> (the default), at startup time
|
|
|
|
+ the <classname>FilterSecurityInterceptor</classname> will evaluate if the provided
|
|
|
|
+ configuration attributes are valid. It does this by checking each configuration
|
|
|
|
+ attribute can be processed by either the
|
|
|
|
+ <interfacename>AccessDecisionManager</interfacename> or the
|
|
|
|
+ <literal>RunAsManager</literal>. If neither of these can process a particular
|
|
|
|
+ configuration attribute, an exception is thrown.</para>
|
|
|
|
+-->
|
|
|
|
+ </section>
|
|
|
|
+ <section xml:id="exception-translation-filter">
|
|
|
|
+ <title>
|
|
|
|
+ <classname>ExceptionTranslationFilter</classname></title>
|
|
|
|
+ <para>The <classname>ExceptionTranslationFilter</classname> sits above the <classname>FilterSecurityInterceptor</classname>
|
|
|
|
+ in the security filter stack. It doesn't do any actual security enforcement itself,
|
|
|
|
+ but handles exceptions thrown by the security interceptors and provides suitable
|
|
|
|
+ and HTTP responses.
|
|
|
|
+ <programlisting language="xml"><![CDATA[
|
|
|
|
+<bean id="exceptionTranslationFilter"
|
|
|
|
+ class="org.springframework.security.web.access.ExceptionTranslationFilter">
|
|
|
|
+ <property name="authenticationEntryPoint" ref="authenticationEntryPoint"/>
|
|
|
|
+ <property name="accessDeniedHandler" ref="accessDeniedHandler"/>
|
|
|
|
+</bean>
|
|
|
|
+
|
|
|
|
+<bean id="authenticationEntryPoint"
|
|
|
|
+ class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
|
|
|
|
+ <property name="loginFormUrl" value="/login.jsp"/>
|
|
|
|
+</bean>
|
|
|
|
+
|
|
|
|
+<bean id="accessDeniedHandler"
|
|
|
|
+ class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
|
|
|
|
+ <property name="errorPage" value="/accessDenied.htm"/>
|
|
|
|
+</bean>
|
|
|
|
+]]></programlisting>
|
|
|
|
+ </para>
|
|
|
|
+ <section xml:id="auth-entry-point">
|
|
|
|
+ <title><interfacename>AuthenticationEntryPoint</interfacename></title>
|
|
|
|
+ <para>
|
|
|
|
+ The <interfacename>AuthenticationEntryPoint</interfacename> will be
|
|
|
|
+ called if the user requests a secure HTTP resource but they are not authenticated. An
|
|
|
|
+ appropriate <exceptionname>AuthenticationException</exceptionname> or
|
|
|
|
+ <exceptionname>AccessDeniedException</exceptionname> will be thrown by a security
|
|
|
|
+ interceptor further down the call stack, triggering the
|
|
|
|
+ <methodname>commence</methodname> method on the entry point. This does the job of
|
|
|
|
+ presenting the appropriate response to the user so that authentication can begin. The
|
|
|
|
+ one we've used here is <classname>LoginUrlAuthenticationEntryPoint</classname>, which
|
|
|
|
+ redirects the request to a different URL (typically a login page). The actual
|
|
|
|
+ implementation used will depend on the authentication mechanism you want to be used in
|
|
|
|
+ your application. </para>
|
|
|
|
+ </section>
|
|
|
|
+ <section xml:id="access-denied-handler">
|
|
|
|
+ <title><interfacename>AccessDeniedHandler</interfacename></title>
|
|
|
|
+ <para>What happens if a user is already authenticated an they try to access a protected
|
|
|
|
+ resource? In normal usage, this shouldn't happen because the application workflow
|
|
|
|
+ should be restricted to operations to which a user has access. For example, an HTML
|
|
|
|
+ link to an administration page might be hidden from users who do not have an admin
|
|
|
|
+ role. You can't rely on hiding links for security though, as there's always a
|
|
|
|
+ possibility that a user will just enter the URL directly in an attempt to bypass the
|
|
|
|
+ restrictions. Or they might modify a RESTful URL to change some of the argument
|
|
|
|
+ values. Your application must be protected against these scenarios or it will
|
|
|
|
+ definitely be insecure. You will typically use simple web layer security to apply
|
|
|
|
+ constraints to basic URLs and use more specific method-based security on your
|
|
|
|
+ service layer interfaces to really nail down what is permissible.</para>
|
|
|
|
+ <para>If an <exceptionname>AccessDeniedException</exceptionname> is thrown and a user
|
|
|
|
+ has already been authenticated, then this means that an operation has been attempted
|
|
|
|
+ for which they don't have enough permissions. In this case,
|
|
|
|
+ <classname>ExceptionTranslationFilter</classname> will invoke a second strategy,
|
|
|
|
+ the <interfacename>AccessDeniedHandler</interfacename>. By default, an
|
|
|
|
+ <classname>AccessDeniedHandlerImpl</classname> is used, which just sends a 403 (Forbidden)
|
|
|
|
+ response to the client. Alternatively you can configure an instance explicitly (as in
|
|
|
|
+ the above example) and set an error page URL which it will forwards the request to
|
|
|
|
+ <footnote><para>We use a forward so that the SecurityContextHolder still contains details of the principal,
|
|
|
|
+ which may be useful for displaying to the user. In old releases of Spring Security we relied upon the servlet
|
|
|
|
+ container to handle a 403 error message, which lacked this useful contextual information.</para></footnote>.
|
|
|
|
+ This can be a simple <quote>access denied</quote> page, such as a JSP, or it could be
|
|
|
|
+ a more complex handler such as an MVC controller. And of course, you can implement the
|
|
|
|
+ interface yourself and use your own implementation.
|
|
|
|
+ </para>
|
|
|
|
+ <para>It's also possible to supply a custom <interfacename>AccessDeniedHandler</interfacename> when you're
|
|
|
|
+ using the namespace to configure your application. See <link xlink:href="#nsa-access-denied-handler"/>.</para>
|
|
|
|
+ </section>
|
|
|
|
+ <section xml:id="security-context-persistence-filter">
|
|
|
|
+ <title><classname>SecurityContextPersistenceFilter</classname></title>
|
|
|
|
+ <para>
|
|
|
|
+ We covered the purpose of this all-important filter in <link xlink:href="#tech-intro-sec-context-persistence"/> so
|
|
|
|
+ you might want to re-read that section at this point. Let's first take a look at how you would configure it
|
|
|
|
+ for use with a <classname>FilterChainProxy</classname>. A basic configuration only requires the bean itself
|
|
|
|
+ <programlisting><![CDATA[
|
|
|
|
+<bean id="securityContextPersistenceFilter"
|
|
|
|
+ class="org.springframework.security.web.context.SecurityContextPersistenceFilter"/>
|
|
|
|
+ ]]></programlisting>
|
|
|
|
+ As we saw previously, this filter has two main tasks. It is responsible for storage of the <classname>SecurityContext</classname>
|
|
|
|
+ contents between HTTP requests and for clearing the <classname>SecurityContextHolder</classname> when a request is completed.
|
|
|
|
+ Clearing the <classname>ThreadLocal</classname> in which the context is stored is essential, as it might otherwise be possible for
|
|
|
|
+ a thread to be replaced into the servlet container's thread pool, with the security context for a particular user still
|
|
|
|
+ attached. This thread might then be used at a later stage, performing operations with the wrong credentials.
|
|
|
|
+ </para>
|
|
|
|
+
|
|
|
|
+ <section xml:id="security-context-repository">
|
|
|
|
+ <title><interfacename>SecurityContextRepository</interfacename></title>
|
|
|
|
+ <para>From Spring Security 3.0, the job of loading and storing the security context is now delegated
|
|
|
|
+ to a separate strategy interface:
|
|
|
|
+<programlisting language="java">
|
|
|
|
+public interface SecurityContextRepository {
|
|
|
|
+ SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder);
|
|
|
|
+ void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response);
|
|
|
|
+}
|
|
|
|
+</programlisting>
|
|
|
|
+ The <classname>HttpRequestResponseHolder</classname> is simply a container for the incoming request and
|
|
|
|
+ response objects, allowing the implementation to replace these with wrapper classes. The
|
|
|
|
+ returned contents will be passed to the filter chain.
|
|
|
|
+ </para>
|
|
|
|
+ <para>
|
|
|
|
+ The default implementation is <classname>HttpSessionSecurityContextRepository</classname>, which
|
|
|
|
+ stores the security context as an <interfacename>HttpSession</interfacename> attribute
|
|
|
|
+ <footnote><para>In Spring Security 2.0 and earlier, this filter was called
|
|
|
|
+ <classname>HttpSessionContextIntegrationFilter</classname> and performed all the work of
|
|
|
|
+ storing the context was performed by the filter itself. If you were familiar with this class,
|
|
|
|
+ then most of the configuration options which were available can now be found on
|
|
|
|
+ <classname>HttpSessionSecurityContextRepository</classname>.
|
|
|
|
+ </para></footnote>.
|
|
|
|
+ The most important configuration parameter for this implementation is the
|
|
|
|
+ <literal>allowSessionCreation</literal> property, which defaults to <literal>true</literal>, thus
|
|
|
|
+ allowing the class to create a session if it needs one to store the security context for an authenticated
|
|
|
|
+ user (it won't create one unless authentication has taken place and the contents of the security context have changed).
|
|
|
|
+ If you don't want a session to be created, then you can set this property to <literal>false</literal>:
|
|
|
|
+ <programlisting language="xml"><![CDATA[
|
|
|
|
+<bean id="securityContextPersistenceFilter"
|
|
|
|
+ class="org.springframework.security.web.context.SecurityContextPersistenceFilter">
|
|
|
|
+ <property name='securityContextRepository'>
|
|
|
|
+ <bean class='org.springframework.security.web.context.HttpSessionSecurityContextRepository'>
|
|
|
|
+ <property name='allowSessionCreation' value='false' />
|
|
|
|
+ </bean>
|
|
|
|
+ </property>
|
|
|
|
+</bean>
|
|
|
|
+ ]]></programlisting>
|
|
|
|
+
|
|
|
|
+ Alternatively you could provide a null implementation of the <interfacename>SecurityContextRepository</interfacename> interface.
|
|
|
|
+ </para>
|
|
|
|
+ </section>
|
|
|
|
+ </section>
|
|
|
|
+ <section xml:id="form-login-filter">
|
|
|
|
+ <title><classname>UsernamePasswordAuthenticationProcessingFilter</classname></title>
|
|
|
|
+ <para>We've now seen the three main filters which are always present in a Spring Security web configuration. These
|
|
|
|
+ are also the three which are automatically created by the namespace <literal><http></literal> element and
|
|
|
|
+ cannot be substituted with alternatives. The only thing that's missing now is an actual authentication mechanism, something
|
|
|
|
+ that will allow a user to authenticate. This filter is the most commonly used authentication filter and the one
|
|
|
|
+ that is most often customized
|
|
|
|
+ <footnote><para>For historical reasons, prior to Spring Security 3.0, this filter was called
|
|
|
|
+ <classname>AuthenticationProcessingFilter</classname> and the entry point was called
|
|
|
|
+ <classname>AuthenticationProcessingFilterEntryPoint</classname>. Since the framework now supports
|
|
|
|
+ many different forms of authentication, they have both been given more specific names in 3.0.</para></footnote>.
|
|
|
|
+ It also provides the implementation used by the <form-login> element from the namespace.
|
|
|
|
+ There are three stages required to configure it.
|
|
|
|
+ <orderedlist>
|
|
|
|
+ <listitem><para>Configure a <classname>LoginUrlAuthenticationEntryPoint</classname> with the URL
|
|
|
|
+ of the login page, just as we did above, and set it on the <classname>ExceptionTranslationFilter</classname>.
|
|
|
|
+ </para>
|
|
|
|
+ </listitem>
|
|
|
|
+ <listitem><para>Implement the login page (using a JSP or MVC controller).</para></listitem>
|
|
|
|
+ <listitem><para>Configure an instance of <classname>UsernamePasswordAuthenticationProcessingFilter</classname> in the application context</para></listitem>
|
|
|
|
+ <listitem><para>Add the filter bean to your filter chain proxy (making sure you pay attention to the order). <!-- TODO: link --></para></listitem>
|
|
|
|
+ </orderedlist>
|
|
|
|
+ The login form simply contains <literal>j_username</literal> and
|
|
|
|
+ <literal>j_password</literal> input fields, and posts to the URL that is
|
|
|
|
+ monitored by the filter (by default this is <literal>/j_spring_security_check</literal>).
|
|
|
|
+ The basic filter configuration looks something like this:
|
|
|
|
+ <programlisting><![CDATA[
|
|
|
|
+<bean id="authenticationProcessingFilter" class=
|
|
|
|
+"org.springframework.security.web.authentication.UsernamePasswordAuthenticationProcessingFilter">
|
|
|
|
+ <property name="authenticationManager" ref="authenticationManager"/>
|
|
|
|
+ <property name="filterProcessesUrl" value="/j_spring_security_check"/>
|
|
|
|
+</bean> ]]>
|
|
|
|
+ </programlisting>
|
|
|
|
+ </para>
|
|
|
|
+
|
|
|
|
+ <section xml:id="form-login-flow-handling">
|
|
|
|
+ <title>Application Flow on Authentication Success and Failure</title>
|
|
|
|
+ <para>
|
|
|
|
+ The filter calls the configured <interfacename>AuthenticationManager</interfacename>
|
|
|
|
+ processes each authentication request. The destination following a successful authentication
|
|
|
|
+ or an authentication failure is controlled by the <interfacename>AuthenticationSuccessHandler</interfacename>
|
|
|
|
+ and <interfacename>AuthenticationFailureHandler</interfacename> strategy interfaces, respectively.
|
|
|
|
+ The filter has properties which allow you to set these so you can customize the behaviour as
|
|
|
|
+ much as you want
|
|
|
|
+ <footnote><para>In versions prior to 3.0, the application flow at this point had evolved to a stage
|
|
|
|
+ was controlled by a mix of properties on this class and strategy plugins. The
|
|
|
|
+ decision was made for 3.0 to refactor the code to make these two strategies entirely responsible.
|
|
|
|
+ </para></footnote>.
|
|
|
|
+ Some standard implementations are supplied such as
|
|
|
|
+ <classname>SimpleUrlAuthenticationSuccessHandler</classname>,
|
|
|
|
+ <classname>SavedRequestAwareAuthenticationSuccessHandler</classname>,
|
|
|
|
+ <classname>SimpleUrlAuthenticationFailureHandler</classname> and
|
|
|
|
+ <classname>ExceptionMappingAuthenticationFailureHandler</classname>. Have a look at the Javadoc
|
|
|
|
+ for these classes to see how they work.
|
|
|
|
+ </para>
|
|
|
|
+
|
|
|
|
+ <para>If authentication is successful, the resulting
|
|
|
|
+ <interfacename>Authentication</interfacename> object will be placed into the
|
|
|
|
+ <classname>SecurityContextHolder</classname>.
|
|
|
|
+ The configured AuthenticationSuccessHandler will then be called to either redirect or forward
|
|
|
|
+ the user to the approprate destination. By default a <classname>SavedRequestAwareAuthenticationSuccessHandler</classname>
|
|
|
|
+ is used, which means that the user will be redirected to the original destination they requested before they were asked to
|
|
|
|
+ login.
|
|
|
|
+ <note>
|
|
|
|
+ <para>
|
|
|
|
+ The <classname>ExceptionTranslationFilter</classname> caches the original request a user makes.
|
|
|
|
+ When the user authenticates, the request handler makes use of this cached request to obtain the original
|
|
|
|
+ URL and redirect to it. The original request is then rebuilt and used as an alternative.
|
|
|
|
+ </para>
|
|
|
|
+ </note>
|
|
|
|
+ If authentication fails, the configured <interfacename>AuthenticationFailureHandler</interfacename> will be invoked.
|
|
|
|
+ </para>
|
|
|
|
+ </section>
|
|
|
|
+ </section>
|
|
|
|
+ </section>
|
|
|
|
+</chapter>
|