spring-security/src/docbkx/common-auth-services.xml

463 lines
24 KiB
XML

<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="authentication-common-auth-services" xmlns:xlink="http://www.w3.org/1999/xlink">
<info><title>Common Authentication Services</title></info>
<section xml:id="mechanisms-providers-entry-points">
<info>
<title>Mechanisms, Providers and Entry Points</title>
</info>
<para>To use Spring Security's authentication services,
you'll usually need to configure a web filter, together
with an <classname>AuthenticationProvider</classname> and
<interfacename>AuthenticationEntryPoint</interfacename>. 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).</para>
<para>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:</para>
<para><programlisting><![CDATA[
<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>]]>
</programlisting></para>
<para>The above declarations will cause every web request to be passed
through to the bean called <literal>filterChainProxy</literal>
which will usually be an instance of Spring Security's
<classname>FilterChainProxy</classname>.
As explained in the filters section of this reference guide, the
<classname>FilterChainProxy</classname> 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:</para>
<para><programlisting><![CDATA[
<bean id="filterChainProxy"
class="org.springframework.security.web.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>]]></programlisting></para>
<para>The <literal>filter-chain-map</literal> syntax from the security namespace
allows you to define the mapping from URLs to filter chains, using a sequence of
<literal>filter-chain</literal> child elements. Each of these defines a set of URLs using
the <literal>pattern</literal> attribute and a chain of filters using the <literal>filters</literal>
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 <literal>id</literal> 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
<literal>httpSessionContextIntegrationFilter</literal>,
<literal>logoutFilter</literal> 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.</para>
<para>In our example we have the
<literal>UsernamePasswordAuthenticationProcessingFilter</literal> and
<literal>BasicProcessingFilter</literal> 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
<literal>DigestProcessingFilter</literal> or
<literal>CasProcessingFilter</literal>. Refer to the individual
chapters of this part of the reference guide to learn how to configure
each of these authentication mechanisms.</para>
<para>Recall that
<classname>HttpSessionContextIntegrationFilter</classname> keeps the
contents of the <interfacename>SecurityContext</interfacename> 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 <literal>GrantedAuthority[]</literal>s)
after the initial authentication step? Let's look at how that is
handled now.</para>
<para>The major authorization provider for secure objects has
previously been introduced as
<classname>AbstractSecurityInterceptor</classname>. This class needs to
have access to an <interfacename>AuthenticationManager</interfacename>. It also
has configurable settings to indicate whether an
<interfacename>Authentication</interfacename> object should be re-authenticated on
each secure object invocation. By default it just accepts any
<interfacename>Authentication</interfacename> inside the
<classname>SecurityContextHolder</classname> is authenticated if
<literal>Authentication.isAuthenticated()</literal> 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
<literal>AbstractSecurityInterceptor.alwaysReauthenticate</literal>
property to true.</para>
<para>You might be asking yourself, "what's this
<interfacename>AuthenticationManager</interfacename>?". We haven't explored it
before, but we have discussed the concept of an
<classname>AuthenticationProvider</classname>. Quite simply, an
<interfacename>AuthenticationManager</interfacename> 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
<interfacename>AuthenticationManager</interfacename> implementation
shipped with Spring Security, so let's look at how it's configured for
the example we're using in this chapter:</para>
<para><programlisting>&lt;bean id="authenticationManager"
class="org.springframework.security.authentication.ProviderManager"&gt;
&lt;property name="providers"&gt;
&lt;list&gt;
&lt;ref local="daoAuthenticationProvider"/&gt;
&lt;ref local="anonymousAuthenticationProvider"/&gt;
&lt;ref local="rememberMeAuthenticationProvider"/&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;/bean&gt;</programlisting></para>
<para>It's probably worth mentioning at this point that your
authentication mechanisms (which are usually filters) are also
injected with a reference to the
<interfacename>AuthenticationManager</interfacename>. So both
<classname>AbstractSecurityInterceptor</classname> as well as the
authentication mechanisms will use the above
<literal>ProviderManager</literal> to poll a list of
<classname>AuthenticationProvider</classname>s.</para>
<para>In our example we have three providers. They are tried in the
order shown (which is implied by the use of a <literal>List</literal>
instead of a <literal>Set</literal>), with each provider able to
attempt authentication, or skip authentication by simply returning
<literal>null</literal>. If all implementations return null, the
<literal>ProviderManager</literal> will throw a suitable exception. If
you're interested in learning more about chaining providers, please
refer to the <literal>ProviderManager</literal> JavaDocs.</para>
<para>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
<literal>DaoAuthenticationProvider</literal> 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
<classname>AuthenticationProvider</classname>. 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
<literal>CasAuthenticationProvider</literal>. A further example of a
one-to-one mapping would be the LDAP authentication mechanism, which
can only be processed an the
<literal>LdapAuthenticationProvider</literal>. 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 <literal>ProviderNotFoundException</literal> when an attempt
to authenticate is made.</para>
<para>After configuring the correct authentication mechanisms in the
<classname>FilterChainProxy</classname>, and ensuring that a corresponding
<classname>AuthenticationProvider</classname> is registered in the
<literal>ProviderManager</literal>, your last step is to configure an
<interfacename>AuthenticationEntryPoint</interfacename>. Recall that earlier we
discussed the role of <classname>ExceptionTranslationFilter</classname>,
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:</para>
<para><programlisting><![CDATA[
<bean id="exceptionTranslationFilter"
class="org.springframework.security.web.access.ExceptionTranslationFilter">
<property name="authenticationEntryPoint" ref="authenticationProcessingFilterEntryPoint"/>
<property name="accessDeniedHandler">
<bean class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
<property name="errorPage" value="/accessDenied.jsp"/>
</bean>
</property>
</bean>
<bean id="authenticationProcessingFilterEntryPoint"
class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<property name="loginFormUrl" value="/login.jsp"/>
<property name="forceHttps">< value="false"/>
</bean>]]></programlisting></para>
<para>Notice that the <classname>ExceptionTranslationFilter</classname>
requires two collaborators. The first,
<literal>AccessDeniedHandlerImpl</literal>, uses a
<literal>RequestDispatcher</literal> forward to display the specified
access denied error page. We use a forward so that the
<classname>SecurityContextHolder</classname> 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). <literal>AccessDeniedHandlerImpl</literal> will also set
the HTTP header to 403, which is the official error code to indicate
access denied. In the case of the
<literal>AuthentionEntryPoint</literal>, 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
<literal>AuthenticationProcessinFilterEntryPoint</literal> and the URL
of the login page. Your application will usually only have one entry
point, and most authentication approaches define their own specific
<interfacename>AuthenticationEntryPoint</interfacename>. Details of which entry
point to use for each authentication approach is discussed in the
authentication approach-specific chapters of this reference
guide.</para>
</section>
<section xml:id="userdetails-and-associated-types">
<info><title>UserDetails and Associated Types</title></info>
<para>As mentioned in the first part of the reference guide, most
authentication providers take advantage of the
<interfacename>UserDetails</interfacename> and
<interfacename>UserDetailsService</interfacename> interfaces. The contract for
this latter interface consists of a single method:</para>
<para><programlisting>
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException;
</programlisting></para>
<para>The returned <interfacename>UserDetails</interfacename> 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
<interfacename>UserDetailsService</interfacename>, even if the username and
password are not actually used as part of the authentication decision.
Generally such providers will be using the returned
<interfacename>UserDetails</interfacename> object just for its
<literal>GrantedAuthority[]</literal> information, because some other
system (like LDAP or X509 or CAS etc) has undertaken the
responsibility of actually validating the credentials.</para>
<para>A single concrete implementation of
<interfacename>UserDetails</interfacename> is provided with Spring Security, being
the <literal>User</literal> class. Spring Security users will need to
decide when writing their <interfacename>UserDetailsService</interfacename> what
concrete <interfacename>UserDetails</interfacename> class to return. In most cases
<literal>User</literal> will be used directly or subclassed, although
special circumstances (such as object relational mappers) may require
users to write their own <interfacename>UserDetails</interfacename> 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
<interfacename>UserDetails</interfacename> 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.</para>
<para>Given <interfacename>UserDetailsService</interfacename> 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.</para>
<section xml:id="in-memory-service">
<info><title>In-Memory Authentication</title></info>
<para>Whilst it is easy to use create a custom
<interfacename>UserDetailsService</interfacename> 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 <interfacename>UserDetailsService</interfacename>
implementations. For this sort of situation, a simple option is to
use the <literal>user-service</literal> element from the security
<link xlink:href="#namespace-minimal" >namespace</link>:
<programlisting><![CDATA[
<user-service id="userDetailsService">
<user name="jimi" password="jimispassword" authorities="ROLE_USER, ROLE_ADMIN" />
<user name="bob" password="bobspassword" authorities="ROLE_USER" />
</user-service>
]]>
</programlisting>
This also suppots the use of an external properties file:
<programlisting><![CDATA[
<user-service id="userDetailsService" properties="users.properties"/>
]]>
</programlisting>
The properties file should contain entries in the form
<programlisting>
username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]
</programlisting>
For example
<programlisting>
jimi=jimispassword,ROLE_USER,ROLE_ADMIN,enabled
bob=bobspassword,ROLE_USER,enabled
</programlisting>
</para>
</section>
<section xml:id="jdbc-service">
<info>
<title>JDBC Authentication</title>
</info>
<para>Spring Security also includes a
<interfacename>UserDetailsService</interfacename> 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
<interfacename>UserDetailsService</interfacename> to reuse the mapping files
you've probably already created. Returning to
<literal>JdbcDaoImpl</literal>, an example configuration is shown
below:</para>
<para><programlisting>
<![CDATA[
<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.core.userdetails.jdbc.JdbcDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean> ]]> </programlisting></para>
<para>You can use different relational database management systems
by modifying the <literal>DriverManagerDataSource</literal> shown
above. You can also use a global data source obtained from JNDI, as
per normal Spring options.
</para>
<section xml:id="jdbc-default-schema">
<title>Default User Database Schema</title>
<para>
Irrespective of the database you are using and how
a <literal>DataSource</literal> is obtained, a standard schema must
be in place. The DDL for an HSQL database instance would be:
<programlisting>
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);
</programlisting>
</para>
<para>If the default schema is unsuitable for your needs,
<literal>JdbcDaoImpl</literal> 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 <interfacename>UserDetails</interfacename> implementation returned,
you'd be better off writing your own
<interfacename>UserDetailsService</interfacename>. The base implementation
provided with Spring Security is intended for typical situations,
rather than catering for all possible requirements.</para>
</section>
</section>
</section>
<section xml:id="concurrent-sessions">
<info><title>Concurrent Session Handling</title></info>
<para>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.</para>
<para>To use concurrent session support, you'll need to add the
following to <literal>web.xml</literal>:
<programlisting>
&lt;listener&gt;
&lt;listener-class&gt;org.springframework.security.web.session.HttpSessionEventPublisher&lt;/listener-class&gt;
&lt;/listener&gt;
</programlisting>
</para>
<para>In addition, you will need to add the
<literal>org.springframework.security.web.authentication.concurrent.ConcurrentSessionFilter</literal>
to your <classname>FilterChainProxy</classname>. The
<classname>ConcurrentSessionFilter</classname> requires two
properties, <literal>sessionRegistry</literal>, which generally points
to an instance of <literal>SessionRegistryImpl</literal>, and
<literal>expiredUrl</literal>, which points to the page to display
when a session has expired.</para>
<para>The <literal>web.xml</literal>
<literal>HttpSessionEventPublisher</literal> causes an
<literal>ApplicationEvent</literal> to be published to the Spring
<literal>ApplicationContext</literal> every time a
<literal>HttpSession</literal> commences or terminates. This is
critical, as it allows the <classname>SessionRegistryImpl</classname> to
be notified when a session ends.</para>
<para>You will also need to wire up the
<classname>ConcurrentSessionControllerImpl</classname> and refer to it
from your <literal>ProviderManager</literal> bean:</para>
<para>
<programlisting><![CDATA[
<bean id="authenticationManager"
class="org.springframework.security.authentication.ProviderManager">
<property name="providers">
<!-- your providers go here -->
</property>
<property name="sessionController" ref="concurrentSessionController"/>
</bean>
<bean id="concurrentSessionController"
class="org.springframework.security.authentication.concurrent.ConcurrentSessionControllerImpl">
<property name="maximumSessions" value="1"/>
<property name="sessionRegistry">
<bean class="org.springframework.security.authentication.concurrent.SessionRegistryImpl"/>
<property>
</bean>
]]></programlisting></para>
</section>
<section xml:id="authentication-taglibs">
<info><title>Authentication Tag Libraries</title></info>
<para><literal>AuthenticationTag</literal> is used to simply output a
property of the current <interfacename>Authentication</interfacename> object to the web
page.</para>
<para>The following JSP fragment illustrates how to use the
<literal>AuthenticationTag</literal>:</para>
<para><programlisting>&lt;security:authentication property="principal.username"/&gt;</programlisting></para>
<para>This tag would cause the principal's name to be output. Here we
are assuming the <literal>Authentication.getPrincipal()</literal> is a
<interfacename>UserDetails</interfacename> object, which is generally the case
when using one of Spring Security's stadard <classname>AuthenticationProvider</classname>
implementations.</para>
</section>
</chapter>