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
Spring Security's web infrastructure should only be used by delegating to an
instance of FilterChainProxy
. The security filters should not
be used by themselves. In theory you could 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, but this would be
cumbersome and would clutter up the web.xml
file quickly if you
have a lot of filters. FilterChainProxy
lets us add a single
entry to web.xml
and deal entirely with the application context
file for managing our web security beans. 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"> <constructor-arg> <list> <sec:filter-chain pattern="/restful/**" filters=" securityContextPersistenceFilterWithASCFalse, basicAuthenticationFilter, exceptionTranslationFilter, filterSecurityInterceptor" /> <sec:filter-chain pattern="/**" filters=" securityContextPersistenceFilterWithASCTrue, formLoginFilter, exceptionTranslationFilter, filterSecurityInterceptor" /> </list> </constructor-arg> </bean>
The namespace element filter-chain
is used for convenience
to set up the security filter chain(s) which are required within the application.
[9]. It maps a particular URL pattern to a list of filters built up from the
bean names specified in the filters
element, and combines them in
a bean of type SecurityFilterChain
. The pattern
attribute takes an Ant Paths and the most specific URIs should appear first
[10]. 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.
Note that FilterChainProxy
does not invoke standard filter
lifecycle methods on the filters it is configured with. We recommend you use
Spring's application context lifecycle interfaces as an alternative, just as you
would for any other Spring bean.
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.
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
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)
ConcurrentSessionFilter
, because it uses the
SecurityContextHolder
functionality and needs to update
the SessionRegistry
to reflect ongoing requests
from the principal
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
The JaasApiIntegrationFilter
, if a
JaasAuthenticationToken
is in the
SecurityContextHolder
this will process the
FilterChain
as the Subject
in the
JaasAuthenticationToken
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
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 FilterChainProxy
decides which filter chain a
request should be passed through and also when the
FilterSecurityInterceptor
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.
The Servlet Specification defines several properties for the
HttpServletRequest
which are accessible via getter
methods, and which we might want to match against. These are the
contextPath
, servletPath
,
pathInfo
and queryString
. Spring Security is
only interested in securing paths within the application, so the
contextPath
is ignored. Unfortunately, the servlet spec does not
define exactly what the values of servletPath
and
pathInfo
will contain for a particular request URI. For example,
each path segment of a URL may contain parameters, as defined in RFC 2396[11]. The Specification does not clearly state whether these should be
included in the servletPath
and pathInfo
values 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.[12]. Other variations in the incoming URL are also possible. For example, it
could contain path-traversal sequences (like /../
) or multiple
forward slashes (//
) which could also cause pattern-matches to
fail. Some containers normalize these out before performing the servlet mapping, but
others don't. To protect against issues like these,
FilterChainProxy
uses an
HttpFirewall
strategy to check and wrap the request.
Un-normalized requests are automatically rejected by default, and path parameters
and duplicate slashes are removed for matching purposes.[13]. It is therefore essential that a
FilterChainProxy
is used to manage the security filter chain.
Note that the servletPath
and pathInfo
values
are decoded by the container, so your application should not have any valid paths
which contain semi-colons, as these parts will be removed for matching purposes.
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. The strategy is implemented
in the class AntPathRequestMatcher
which uses Spring's
AntPathMatcher
to perform a case-insensitive match of the
pattern against the concatenated servletPath
and
pathInfo
, ignoring the queryString
.
If for some reason, you need a more powerful matching strategy, you can use
regular expressions. The strategy implementation is then
RegexRequestMatcher
. See the Javadoc for this class for more
information.
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
“deny-by-default” approach where you have a catch-all wildcard
(/**
or **
) defined last and denying access.
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.
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.
As we saw earlier in the namespace chapter, it's possible to use multiple http
elements to define different security configurations for different URL patterns.
Each element creates a filter chain within the internal FilterChainProxy
and the
URL pattern that should be mapped to it. The elements will be added in the order they are declared, so the
most specific patterns must again be declared first. Here's another example, for a similar situation to
that above, where the application supports both a stateless RESTful API and also a normal web application
which users log into using a form.
<!-- Stateless RESTful service using Basic authentication --> <http pattern="/restful/**" create-session="stateless"> <intercept-url pattern='/**' access='ROLE_REMOTE' /> <http-basic /> </http> <!-- Empty filter chain for the login page --> <http pattern="/login.htm*" security="none"/> <!-- Additional filter chain for normal users, matching all other requests --> <http> <intercept-url pattern='/**' access='ROLE_USER' /> <form-login login-page='/login.htm' default-target-url="/home.htm"/> <logout /> </http>
[9] Note that you'll need to include the security namespace in your application
context XML file in order to use this syntax. The older syntax which used a
filter-chain-map
is still supported, but is deprecated in favour of
the constructor argument injection.
[10] Instead of a path pattern, the request-matcher-ref
attribute
can be used to specify a RequestMatcher
instance for more powerful
matching
[11] You have probably seen this when a browser doesn't support cookies and the
jsessionid
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
[12] The original values will be returned once the request leaves the
FilterChainProxy
, so will still be available to the
application.
[13] So, for example, an original request path
/secure;hack=1/somefile.html;hack=2
will be returned as
/secure/somefile.html
.