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.
As mentioned in the first part of the reference guide, most
authentication providers take advantage of the
UserDetails
and
UserDetailsService
interfaces. The contract for
this latter interface consists of a single method:
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException;
The returned UserDetails
is an interface that
provides getters that guarantee non-null provision of basic
authentication information such as the username, password, granted
authorities and whether the user is enabled or disabled. Most
authentication providers will use a
UserDetailsService
, even if the username and
password are not actually used as part of the authentication decision.
Generally such providers will be using the returned
UserDetails
object just for its
GrantedAuthority[]
information, because some other
system (like LDAP or X509 or CAS etc) has undertaken the
responsibility of actually validating the credentials.
A single concrete implementation of
UserDetails
is provided with Spring Security, being
the User
class. Spring Security users will need to
decide when writing their UserDetailsService
what
concrete UserDetails
class to return. In most cases
User
will be used directly or subclassed, although
special circumstances (such as object relational mappers) may require
users to write their own UserDetails
implementation
from scratch. This is not such an unusual situation, and users should
not hesitate to simply return their normal domain object that
represents a user of the system. This is especially common given that
UserDetails
is often used to store additional
principal-related properties (such as their telephone number and email
address), so that they can be easily used by web views.
Given UserDetailsService
is so simple to
implement, it should be easy for users to retrieve authentication
information using a persistence strategy of their choice. Having said
that, Spring Security does include a couple of useful base
implementations, which we'll look at below.
Whilst it is easy to use create a custom
UserDetailsService
implementation that extracts
information from a persistence engine of choice, many applications
do not require such complexity. This is particularly true if you're
undertaking a rapid prototype or just starting integrating Spring
Security, when you don't really want to spend time configuring
databases or writing UserDetailsService
implementations. For this sort of situation, a simple option is to
use the user-service
element from the security
namespace:
<user-service id="userDetailsService"> <user name="jimi" password="jimispassword" authorities="ROLE_USER, ROLE_ADMIN" /> <user name="bob" password="bobspassword" authorities="ROLE_USER" /> </user-service>
This also suppots the use of an external properties file:
<user-service id="userDetailsService" properties="users.properties"/>
The properties file should contain entries in the form
username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]
For example
jimi=jimispassword,ROLE_USER,ROLE_ADMIN,enabled bob=bobspassword,ROLE_USER,enabled
Spring Security also includes a
UserDetailsService
that can obtain authentication
information from a JDBC data source. Internally Spring JDBC is used,
so it avoids the complexity of a fully-featured object relational
mapper (ORM) just to store user details. If your application does
use an ORM tool, you might prefer to write a custom
UserDetailsService
to reuse the mapping files
you've probably already created. Returning to
JdbcDaoImpl
, an example configuration is shown
below:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="org.hsqldb.jdbcDriver"/> <property name="url" value="jdbc:hsqldb:hsql://localhost:9001"/> <property name="username" value="sa"/> <property name="password" value=""/> </bean> <bean id="userDetailsService" class="org.springframework.security.userdetails.jdbc.JdbcDaoImpl"> <property name="dataSource" ref="dataSource"/> </bean>
You can use different relational database management systems
by modifying the DriverManagerDataSource
shown
above. You can also use a global data source obtained from JNDI, as
per normal Spring options.
Irrespective of the database you are using and how
a DataSource
is obtained, a standard schema must
be in place. The DDL for an HSQL database instance would be:
CREATE TABLE users ( username VARCHAR(50) NOT NULL PRIMARY KEY, password VARCHAR(50) NOT NULL, enabled BIT NOT NULL ); CREATE TABLE authorities ( username VARCHAR(50) NOT NULL, authority VARCHAR(50) NOT NULL ); ALTER TABLE authorities ADD CONSTRAINT fk_authorities_users foreign key (username) REFERENCES users(username);
If the default schema is unsuitable for your needs,
JdbcDaoImpl
provides properties that allow
customisation of the SQL statements. Please refer to the JavaDocs for
details, but note that the class is not intended for complex custom subclasses.
If you have a complex schema or would like a
custom UserDetails
implementation returned,
you'd be better off writing your own
UserDetailsService
. The base implementation
provided with Spring Security is intended for typical situations,
rather than catering for all possible requirements.
Spring Security is able to prevent a principal from concurrently authenticating to the same application more than a specified number of times. Many ISVs take advantage of this to enforce licensing, whilst network administrators like this feature because it helps prevent people from sharing login names. You can, for example, stop user "Batman" from logging onto the web application from two different sessions.
To use concurrent session support, you'll need to add the
following to web.xml
:
<listener> <listener-class>org.springframework.security.ui.session.HttpSessionEventPublisher</listener-class> </listener>
In addition, you will need to add the
org.springframework.security.concurrent.ConcurrentSessionFilter
to your FilterChainProxy
. The
ConcurrentSessionFilter
requires two
properties, sessionRegistry
, which generally points
to an instance of SessionRegistryImpl
, and
expiredUrl
, which points to the page to display
when a session has expired.
The web.xml
HttpSessionEventPublisher
causes an
ApplicationEvent
to be published to the Spring
ApplicationContext
every time a
HttpSession
commences or terminates. This is
critical, as it allows the SessionRegistryImpl
to
be notified when a session ends.
You will also need to wire up the
ConcurrentSessionControllerImpl
and refer to it
from your ProviderManager
bean:
<bean id="authenticationManager" class="org.springframework.security.providers.ProviderManager"> <property name="providers"> <!-- your providers go here --> </property> <property name="sessionController" ref="concurrentSessionController"/> </bean> <bean id="concurrentSessionController" class="org.springframework.security.concurrent.ConcurrentSessionControllerImpl"> <property name="maximumSessions" value="1"/> <property name="sessionRegistry"> <bean class="org.springframework.security.concurrent.SessionRegistryImpl"/> <property> </bean>
AuthenticationTag
is used to simply output a
property of the current Authentication
object to the web
page.
The following JSP fragment illustrates how to use the
AuthenticationTag
:
<security:authentication property="principal.username"/>
This tag would cause the principal's name to be output. Here we
are assuming the Authentication.getPrincipal()
is a
UserDetails
object, which is generally the case
when using one of Spring Security's stadard AuthenticationProvider
implementations.