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 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.
We've already seen FilterSecurityInterceptor
briefly when
discussing access-control in
general, and we've already used it with the namespace where the
<intercept-url>
elements are combined to configure it
internally. Now we'll see how to explicitly configure it for use with a
FilterChainProxy
, along with its companion filter
ExceptionTranslationFilter
. A typical configuration example
is shown below:
<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>
FilterSecurityInterceptor
is responsible for handling the
security of HTTP resources. It requires a reference to an
AuthenticationManager
and an
AccessDecisionManager
. It is also supplied with
configuration attributes that apply to different HTTP URL requests. Refer back to the original discussion on these
in the technical introduction.
The FilterSecurityInterceptor
can be configured with
configuration attributes in two ways. The first, which is shown above, is using the
<filter-security-metadata-source>
namespace element. This
is similar to the <filter-chain-map>
used to configure a
FilterChainProxy
but the
<intercept-url>
child elements only use the
pattern
and access
attributes. Commas are used
to delimit the different configuration attributes that apply to each HTTP URL. The
second option is to write your own
SecurityMetadataSource
, but this is beyond the scope of
this document. Irrespective of the approach used, the
SecurityMetadataSource
is responsible for returning a
List<ConfigAttribute>
containing all of the configuration
attributes associated with a single secure HTTP URL.
It should be noted that the
FilterSecurityInterceptor.setSecurityMetadataSource()
method
actually expects an instance of
FilterSecurityMetadataSource
. This is a marker
interface which subclasses SecurityMetadataSource
. It
simply denotes the SecurityMetadataSource
understands
FilterInvocation
s. In the interests of simplicity we'll
continue to refer to the FilterInvocationDefinitionSource
as a SecurityMetadataSource
, as the distinction is of
little relevance to most users.
The SecurityMetadataSource
created by the namespace
syntax obtains the configuration attributes for a particular
FilterInvocation
by matching the request URL against the
configured pattern
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
path-type
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:
<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>
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
/secure/super/
pattern appears higher than the less specific
/secure/
pattern. If they were reversed, the
/secure/
pattern would always match and the
/secure/super/
pattern would never be evaluated.
The ExceptionTranslationFilter
sits above the
FilterSecurityInterceptor
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.
<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>
The AuthenticationEntryPoint
will be called if the
user requests a secure HTTP resource but they are not authenticated. An appropriate
AuthenticationException
or
AccessDeniedException
will be thrown by a
security interceptor further down the call stack, triggering the
commence
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 LoginUrlAuthenticationEntryPoint
,
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.
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.
If an AccessDeniedException
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,
ExceptionTranslationFilter
will invoke a second strategy,
the AccessDeniedHandler
. By default, an
AccessDeniedHandlerImpl
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 [5]. This can be a
simple “access denied” 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.
It's also possible to supply a custom
AccessDeniedHandler
when you're using the
namespace to configure your application. See the namespace appendix for more
details.
We covered the purpose of this all-important filter in #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 FilterChainProxy
. A basic configuration only requires the
bean itself
<bean id="securityContextPersistenceFilter" class="org.springframework.security.web.context.SecurityContextPersistenceFilter"/>
As we saw previously, this filter has two main tasks. It is responsible for
storage of the SecurityContext
contents between HTTP requests and
for clearing the SecurityContextHolder
when a request is
completed. Clearing the ThreadLocal
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.
From Spring Security 3.0, the job of loading and storing the security context is now delegated to a separate strategy interface:
public interface SecurityContextRepository { SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder); void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response); }
The HttpRequestResponseHolder
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.
The default implementation is
HttpSessionSecurityContextRepository
, which stores the
security context as an HttpSession
attribute
[6]. The most important configuration parameter for this
implementation is the allowSessionCreation
property, which
defaults to true
, 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 false
:
<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>
Alternatively you could provide a null implementation of the
SecurityContextRepository
interface.
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 <http>
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 [7]. It also
provides the implementation used by the <form-login> element from the namespace.
There are three stages required to configure it.
Configure
a LoginUrlAuthenticationEntryPoint
with the URL of
the login page, just as we did above, and set it on the
ExceptionTranslationFilter
.
Implement the login page (using a JSP or MVC controller).
Configure an instance of
UsernamePasswordAuthenticationFilter
in the
application context
Add the filter bean to your filter chain proxy (making sure you pay attention to the order).
The login form simply
contains j_username
and j_password
input fields,
and posts to the URL that is monitored by the filter (by default this is
/j_spring_security_check
). The basic filter configuration looks
something like this:
<bean id="authenticationFilter" class= "org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter"> <property name="authenticationManager" ref="authenticationManager"/> <property name="filterProcessesUrl" value="/j_spring_security_check"/> </bean>
The filter calls the configured
AuthenticationManager
to process each
authentication request. The destination following a successful authentication or an
authentication failure is controlled by the
AuthenticationSuccessHandler
and
AuthenticationFailureHandler
strategy interfaces,
respectively. The filter has properties which allow you to set these so you can
customize the behaviour completely [8]. Some standard implementations are supplied
such as SimpleUrlAuthenticationSuccessHandler
,
SavedRequestAwareAuthenticationSuccessHandler
,
SimpleUrlAuthenticationFailureHandler
and
ExceptionMappingAuthenticationFailureHandler
. Have a look
at the Javadoc for these classes to see how they work.
If authentication is successful, the resulting
Authentication
object will be placed into the
SecurityContextHolder
. The configured
AuthenticationSuccessHandler will then be called to either redirect or forward the
user to the approprate destination. By default a
SavedRequestAwareAuthenticationSuccessHandler
is used,
which means that the user will be redirected to the original destination they
requested before they were asked to login.
Note | |
---|---|
The
|
If authentication fails, the configured
AuthenticationFailureHandler
will be invoked.
[5] 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.
[6] In Spring Security 2.0 and earlier, this filter was called
HttpSessionContextIntegrationFilter
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
HttpSessionSecurityContextRepository
.
[7] For
historical reasons, prior to Spring Security 3.0, this filter was called
AuthenticationProcessingFilter
and the entry point
was called AuthenticationProcessingFilterEntryPoint
.
Since the framework now supports many different forms of authentication, they
have both been given more specific names in 3.0.
[8] 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.