Spring Security's web infrastructure is based entirely on standard servlet filters. It
doesn't use servlets or any other servlet-based frameworks (such as Spring MVC) internally, so
it has no strong links to any particular web technology. It deals in
HttpServletRequest
s and HttpServletResponse
s and
doesn't care whether the requests come from a browser, a web service client, an
HttpInvoker
or an AJAX application.
Spring Security maintains a filter chain internally where each of the filters has a particular responsibility and filters are added or removed from the configuration depending on which services are required. The ordering of the filters is important as there are dependencies between them. If you have been using namespace configuration, then the filters are automatically configured for you and you don't have to define any Spring beans explicitly but here may be times when you want full control over the security filter chain, either because you are using features which aren't supported in the namespace, or you are using your own customized versions of classes.
When using servlet filters, you obviously need to declare them in your
web.xml
, or they will be ignored by the servlet container. In Spring
Security, the filter classes are also Spring beans defined in the application context and thus
able to take advantage of Spring's rich dependency-injection facilities and lifecycle
interfaces. Spring's DelegatingFilterProxy
provides the link between
web.xml
and the application context.
When using DelegatingFilterProxy
, you will see something like this
in the web.xml
file:
<filter> <filter-name>myFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>myFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
Notice that the filter is actually a
DelegatingFilterProxy
, and not the class that will actually implement the
logic of the filter. What DelegatingFilterProxy
does is delegate the
Filter
's methods through to a bean which is obtained from the
Spring application context. This enables the bean to benefit from the Spring web application
context lifecycle support and configuration flexibility. The bean must implement
javax.servlet.Filter
and it must have the same name as that
in the filter-name
element. Read the Javadoc for
DelegatingFilterProxy
for more information
It should now be clear that you can declare each Spring Security filter bean that you
require in your application context file and add a corresponding
DelegatingFilterProxy
entry to web.xml
for each
filter, making sure that they are ordered correctly. This is a cumbersome approach and
clutters up the web.xml
file quickly if we have a lot of filters. We
would prefer to just add a single entry to web.xml
and deal entirely with
the application context file for managing our web security beans. This is where Spring
Secuiryt's FilterChainProxy
comes in. It is wired using a
DelegatingFilterProxy
, just like in the example above, but with the
filter-name
set to the bean name “filterChainProxy”. The
filter chain is then declared in the application context with the same bean name. Here's an
example:
<bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy"> <sec:filter-chain-map path-type="ant"> <sec:filter-chain pattern="/webServices/**" filters=" securityContextPersistenceFilterWithASCFalse, basicAuthenticationFilter, exceptionTranslationFilter, filterSecurityInterceptor" /> <sec:filter-chain pattern="/**" filters=" securityContextPersistenceFilterWithASCTrue, formLoginFilter, exceptionTranslationFilter, filterSecurityInterceptor" /> </sec:filter-chain-map> </bean>
The namespace element filter-chain-map
is used
to set up the security filter chain(s) which are required within the
application[4]. It maps a
particular URL pattern to a chain of filters built up from the bean names specified in the
filters
element. Both regular expressions and Ant Paths are supported,
and the most specific URIs appear first. At runtime the
FilterChainProxy
will locate the first URI pattern that matches the
current web request and the list of filter beans specified by the filters
attribute will be applied to that request. The filters will be invoked in the order they are
defined, so you have complete control over the filter chain which is applied to a particular
URL.
You may have noticed we have declared two
SecurityContextPersistenceFilter
s in the filter chain
(ASC
is short for allowSessionCreation
, a property of
SecurityContextPersistenceFilter
). As web services will never present
a jsessionid
on future requests, creating HttpSession
s
for such user agents would be wasteful. If you had a high-volume application which required
maximum scalability, we recommend you use the approach shown above. For smaller applications,
using a single SecurityContextPersistenceFilter
(with its default
allowSessionCreation
as true
) would likely be
sufficient.
In relation to lifecycle issues, the FilterChainProxy
will always
delegate init(FilterConfig)
and destroy()
methods through to the underlaying Filter
s if such methods are
called against FilterChainProxy
itself. In this case,
FilterChainProxy
guarantees to only initialize and destroy each
Filter
bean once, no matter how many times it is declared in the filter
chain(s). You control the overall choice as to whether these methods are called or not via the
targetFilterLifecycle
initialization parameter of
DelegatingFilterProxy
. By default this property is
false
and servlet container lifecycle invocations are not delegated
through DelegatingFilterProxy
.
When we looked at how to set up web security using namespace configuration, we used a DelegatingFilterProxy
with the
name “springSecurityFilterChain”. You should now be able to see that this is the
name of the FilterChainProxy
which is created by the namespace.
As with the namespace, you can use the attribute filters = "none"
as
an alternative to supplying a filter bean list. This will omit the request pattern from the
security filter chain entirely. Note that anything matching this path will then have no
authentication or authorization services applied and will be freely accessible. If you want
to make use of the contents of the SecurityContext
contents during a
request, then it must have passed through the security filter chain. Otherwise the
SecurityContextHolder
will not have been populated and the contents
will be null.
The order that filters are defined in the chain is very important. Irrespective of which filters you are actually using, the order should be as follows:
ChannelProcessingFilter
, because
it might need to redirect to a different
protocol
ConcurrentSessionFilter
,
because it doesn't use any SecurityContextHolder
functionality
but needs to update the SessionRegistry
to reflect
ongoing requests from the
principal
SecurityContextPersistenceFilter
,
so a SecurityContext
can be set up in the
SecurityContextHolder
at the beginning of a web request, and
any changes to the SecurityContext
can be copied to the
HttpSession
when the web request ends (ready for use with the next
web request)
Authentication processing mechanisms -
UsernamePasswordAuthenticationFilter
,
CasAuthenticationFilter
,
BasicAuthenticationFilter
etc - so that the
SecurityContextHolder
can be modified to contain a valid
Authentication
request
token
The
SecurityContextHolderAwareRequestFilter
, if you are using it to
install a Spring Security aware HttpServletRequestWrapper
into your
servlet
container
RememberMeAuthenticationFilter
,
so that if no earlier authentication processing mechanism updated the
SecurityContextHolder
, and the request presents a cookie that
enables remember-me services to take place, a suitable remembered
Authentication
object will be put
there
AnonymousAuthenticationFilter
,
so that if no earlier authentication processing mechanism updated the
SecurityContextHolder
, an anonymous
Authentication
object will be put
there
ExceptionTranslationFilter
,
to catch any Spring Security exceptions so that either an HTTP error response can be
returned or an appropriate AuthenticationEntryPoint
can
be
launched
FilterSecurityInterceptor
,
to protect web URIs and raise exceptions when access is
denied
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
SecurityContextHolder
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.
[4] Note that you'll need to include the security namespace in your application context XML file in order to use this syntax.