Table of Contents
To use Spring Security's authentication services,
you'll usually need to configure a web filter, together
with an AuthenticationProvider
and
AuthenticationEntryPoint
. In this section we are
going to explore an example application that needs to support both
form-based authentication (so a nice HTML page is presented to a
user for them to login) and BASIC authentication (so a web service
or similar can access protected resources).
In the web.xml, this application will need a single Spring Security filter in order to use the FilterChainProxy. Nearly every Spring Security application will have such an entry, and it looks like this:
<filter> <filter-name>filterChainProxy</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>filterChainProxy</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
The above declarations will cause every web request to be passed
through to the bean called filterChainProxy
which will usually be an instance of Spring Security's
FilterChainProxy
.
As explained in the filters section of this reference guide, the
FilterChainProxy
is a generally-useful class
that enables web requests to be passed to different filters based on
URL patterns. Those delegated filters are managed inside the
application context, so they can benefit from dependency injection.
Let's have a look at what the FilterChainProxy bean definition would
look like inside your application context:
<bean id="filterChainProxy" class="org.springframework.security.util.FilterChainProxy"> <security:filter-chain-map path-type="ant"> <security:filter-chain pattern="/**" filters="httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,basicProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor,switchUserProcessingFilter"/> </security:filter-chain-map> </bean>
The filter-chain-map
syntax from the security namespace
allows you to define the mapping from URLs to filter chains, using a sequence of
filter-chain
child elements. Each of these defines a set of URLs using
the pattern
attribute and a chain of filters using the filters
attribute.What's important to note at this stage is that a series of filters will be
run - in the order specified by the declaration - and each of those
filters are actually the id
of another
bean in the application context. So, in our case some extra beans
will also appear in the application context, and they'll be named
httpSessionContextIntegrationFilter
,
logoutFilter
and so on. The order that the filters
should appear is discussed in the filters section of the reference
guide - although they are correct in the above example.
In our example we have the
AuthenticationProcessingFilter
and
BasicProcessingFilter
being used. These are the
"authentication mechanisms" that respond to form-based authentication
and BASIC HTTP header-based authentication respectively (we discussed
the role of authentication mechanisms earlier in this reference
guide). If you weren't using form or BASIC authentication, neither of
these beans would be defined. You'd instead define filters applicable
to your desired authentication environment, such as
DigestProcessingFilter
or
CasProcessingFilter
. Refer to the individual
chapters of this part of the reference guide to learn how to configure
each of these authentication mechanisms.
Recall that
HttpSessionContextIntegrationFilter
keeps the
contents of the SecurityContext
between invocations
inside an HTTP session. This means the authentication mechanisms are
only used once, being when the principal initially tries to
authenticate. The rest of the time the authentication mechanisms sit
there and silently pass the request through to the next filter in the
chain. That is a practical requirement due to the fact that few
authentication approaches present credentials on each and every call
(BASIC authentication being a notable exception), but what happens if
a principal's account gets cancelled or disabled or otherwise changed
(eg an increase or decrease in GrantedAuthority[]
s)
after the initial authentication step? Let's look at how that is
handled now.
The major authorization provider for secure objects has
previously been introduced as
AbstractSecurityInterceptor
. This class needs to
have access to an AuthenticationManager
. It also
has configurable settings to indicate whether an
Authentication
object should be re-authenticated on
each secure object invocation. By default it just accepts any
Authentication
inside the
SecurityContextHolder
is authenticated if
Authentication.isAuthenticated()
returns true. This
is great for performance, but not ideal if you want to ensure
up-to-the-moment authentication validity. For such cases you'll
probably want to set the
AbstractSecurityInterceptor.alwaysReauthenticate
property to true.
You might be asking yourself, "what's this
AuthenticationManager
?". We haven't explored it
before, but we have discussed the concept of an
AuthenticationProvider
. Quite simply, an
AuthenticationManager
is responsible
for passing requests through a chain of AuthenticationProviders. It's
a little like the filter chain we discussed earlier, although there
are some differences. There is only one
AuthenticationManager
implementation
shipped with Spring Security, so let's look at how it's configured for
the example we're using in this chapter:
<bean id="authenticationManager" class="org.springframework.security.providers.ProviderManager"> <property name="providers"> <list> <ref local="daoAuthenticationProvider"/> <ref local="anonymousAuthenticationProvider"/> <ref local="rememberMeAuthenticationProvider"/> </list> </property> </bean>
It's probably worth mentioning at this point that your
authentication mechanisms (which are usually filters) are also
injected with a reference to the
AuthenticationManager
. So both
AbstractSecurityInterceptor
as well as the
authentication mechanisms will use the above
ProviderManager
to poll a list of
AuthenticationProvider
s.
In our example we have three providers. They are tried in the
order shown (which is implied by the use of a List
instead of a Set
), with each provider able to
attempt authentication, or skip authentication by simply returning
null
. If all implementations return null, the
ProviderManager
will throw a suitable exception. If
you're interested in learning more about chaining providers, please
refer to the ProviderManager
JavaDocs.
The providers to use will sometimes be interchangeable with the
authentication mechanisms, whilst at other times they will depend on a
specific authentication mechanism. For example, the
DaoAuthenticationProvider
just needs a string-based
username and password. Various authentication mechanisms result in the
collection of a string-based username and password, including (but not
limited to) BASIC and form authentication. Equally, some
authentication mechanisms create an authentication request object
which can only be interpreted by a single type of
AuthenticationProvider
. An example of this
one-to-one mapping would be JA-SIG CAS, which uses the notion of a
service ticket which can therefore only be authenticated by
CasAuthenticationProvider
. A further example of a
one-to-one mapping would be the LDAP authentication mechanism, which
can only be processed an the
LdapAuthenticationProvider
. The specifics of such
relationships are detailed in the JavaDocs for each class, plus the
authentication approach-specific chapters of this reference guide. You
need not be terribly concerned about this implementation detail,
because if you forget to register a suitable provider, you'll simply
receive a ProviderNotFoundException
when an attempt
to authenticate is made.
After configuring the correct authentication mechanisms in the
FilterChainProxy
, and ensuring that a corresponding
AuthenticationProvider
is registered in the
ProviderManager
, your last step is to configure an
AuthenticationEntryPoint
. Recall that earlier we
discussed the role of ExceptionTranslationFilter
,
which is used when HTTP-based requests should receive back an HTTP
header or HTTP redirect in order to start authentication. Continuing
on with our earlier example:
<bean id="exceptionTranslationFilter" class="org.springframework.security.ui.ExceptionTranslationFilter"> <property name="authenticationEntryPoint" ref="authenticationProcessingFilterEntryPoint"/> <property name="accessDeniedHandler"> <bean class="org.springframework.security.ui.AccessDeniedHandlerImpl"> <property name="errorPage" value="/accessDenied.jsp"/> </bean> </property> </bean> <bean id="authenticationProcessingFilterEntryPoint" class="org.springframework.security.ui.webapp.AuthenticationProcessingFilterEntryPoint"> <property name="loginFormUrl" value="/login.jsp"/> <property name="forceHttps">< value="false"/> </bean>
Notice that the ExceptionTranslationFilter
requires two collaborators. The first,
AccessDeniedHandlerImpl
, uses a
RequestDispatcher
forward to display the specified
access denied error page. We use a forward so that the
SecurityContextHolder
still contains details of the
principal, which may be useful for display 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). AccessDeniedHandlerImpl
will also set
the HTTP header to 403, which is the official error code to indicate
access denied. In the case of the
AuthentionEntryPoint
, here we're setting what
action we would like taken when an unauthenticated principal attempts
to perform a protected operation. Because in our example we're going
to be using form-based authentication, we specify
AuthenticationProcessinFilterEntryPoint
and the URL
of the login page. Your application will usually only have one entry
point, and most authentication approaches define their own specific
AuthenticationEntryPoint
. Details of which entry
point to use for each authentication approach is discussed in the
authentication approach-specific chapters of this reference
guide.