295 lines
22 KiB
XML
295 lines
22 KiB
XML
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="core-services"
|
|
xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
<title>Core Services</title>
|
|
<para> Now that we have a high-level overview of the Spring Security architecture and its core
|
|
classes, let's take a closer look at one or two of the core interfaces and their
|
|
implementations, in particular the <interfacename>AuthenticationManager</interfacename>,
|
|
<interfacename>UserDetailsService</interfacename> and the
|
|
<interfacename>AccessDecisionManager</interfacename>. These crop up regularly throughout the
|
|
remainder of this document so it's important you know how they are configured and how they
|
|
operate. </para>
|
|
<section xml:id="core-services-authentication-manager">
|
|
<title>The <interfacename>AuthenticationManager</interfacename>,
|
|
<classname>ProviderManager</classname> and
|
|
<classname>AuthenticationProvider</classname>s</title>
|
|
<para>The <interfacename>AuthenticationManager</interfacename> is just an interface, so the
|
|
implementation can be anything we choose, but how does it work in practice? What if we
|
|
need to check multiple authentication databases or a combination of different
|
|
authentication services such as a database and an LDAP server?</para>
|
|
<para>The default implementation in Spring Security is called
|
|
<classname>ProviderManager</classname> and rather than handling the authentication
|
|
request itself, it delegates to a list of configured
|
|
<classname>AuthenticationProvider</classname>s, each of which is queried in turn to see
|
|
if it can perform the authentication. Each provider will either throw an exception or
|
|
return a fully populated <interfacename>Authentication</interfacename> object. Remember
|
|
our good friends, <interfacename>UserDetails</interfacename> and
|
|
<interfacename>UserDetailsService</interfacename>? If not, head back to the previous
|
|
chapter and refresh your memory. The most common approach to verifying an authentication
|
|
request is to load the corresponding <interfacename>UserDetails</interfacename> and
|
|
check the loaded password against the one that has been entered by the user. This is the
|
|
approach used by the <classname>DaoAuthenticationProvider</classname> (see below). The
|
|
loaded <interfacename>UserDetails</interfacename> object - and particularly the
|
|
<literal>GrantedAuthority</literal>s it contains - will be used when building the fully
|
|
populated <interfacename>Authentication</interfacename> object which is returned from a
|
|
successful authentication and stored in the <classname>SecurityContext</classname>. </para>
|
|
<para> If you are using the namespace, an instance of <classname>ProviderManager</classname>
|
|
is created and maintained internally, and you add providers to it by using the namespace
|
|
authentication provider elements (see <link xlink:href="#ns-auth-manager">the namespace
|
|
chapter</link>). In this case, you should not declare a
|
|
<classname>ProviderManager</classname> bean in your application context. However, if you
|
|
are not using the namespace then you would declare it like so: <programlisting language="xml"><![CDATA[
|
|
<bean id="authenticationManager"
|
|
class="org.springframework.security.authentication.ProviderManager">
|
|
<property name="providers">
|
|
<list>
|
|
<ref local="daoAuthenticationProvider"/>
|
|
<ref local="anonymousAuthenticationProvider"/>
|
|
<ref local="ldapAuthenticationProvider"/>
|
|
</list>
|
|
</property>
|
|
</bean>]]></programlisting></para>
|
|
<para>In the above example we have three providers. They are tried in the order shown (which
|
|
is implied by the use of a <literal>List</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
|
|
<exceptionname>ProviderNotFoundException</exceptionname>. If you're interested in
|
|
learning more about chaining providers, please refer to the
|
|
<literal>ProviderManager</literal> JavaDocs.</para>
|
|
<para> Authentication mechanisms such as a web form-login processing filter are injected
|
|
with a reference to the <interfacename>ProviderManager</interfacename> and will call it
|
|
to handle their authentication requests. The providers you require will sometimes be
|
|
interchangeable with the authentication mechanisms, while at other times they will
|
|
depend on a specific authentication mechanism. For example,
|
|
<classname>DaoAuthenticationProvider</classname> and
|
|
<classname>LdapAuthenticationProvider</classname> are compatible with any mechanism
|
|
which submits a simple username/password authentication request and so will work with
|
|
form-based logins or HTTP Basic authentication. On the other hand, 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 would
|
|
be JA-SIG CAS, which uses the notion of a service ticket and so can therefore only be
|
|
authenticated by a <classname>CasAuthenticationProvider</classname>. You needn't be too
|
|
concerned about this, 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>
|
|
<section xml:id="core-services-erasing-credentials">
|
|
<title>Erasing Credentials on Successful Authentication</title>
|
|
<para>
|
|
By default (from Spring Security 3.1 onwards) the <classname>ProviderManager</classname>
|
|
will attempt to clear any sensitive credentials information from the
|
|
<interfacename>Authentication</interfacename> object which is returned by a successful
|
|
authentication request. This prevents information like passwords being retained longer
|
|
than necessary.
|
|
</para>
|
|
<para>
|
|
This may cause issues when you are using a cache of user objects, for example, to
|
|
improve performance in a stateless application. If the <interfacename>Authentication</interfacename>
|
|
contains a reference to an object in the cache (such as a <interfacename>UserDetails</interfacename>
|
|
instance) and this has its credentials removed, then it will no longer be possible to authenticate
|
|
against the cached value. You need to take this into account if you are using a cache. An obvious
|
|
solution is to make a copy of the object first, either in the cache implementation or in
|
|
the <interfacename>AuthenticationProvider</interfacename> which creates the returned
|
|
<interfacename>Authentication</interfacename> object. Alternatively, you can disable the
|
|
<literal>eraseCredentialsAfterAuthentication</literal> property on <classname>ProviderManager</classname>.
|
|
See the Javadoc for more information.
|
|
</para>
|
|
</section>
|
|
<section xml:id="core-services-dao-provider">
|
|
<title><literal>DaoAuthenticationProvider</literal></title>
|
|
<para>The simplest <interfacename>AuthenticationProvider</interfacename> implemented by
|
|
Spring Security is <literal>DaoAuthenticationProvider</literal>, which is also one
|
|
of the earliest supported by the framework. It leverages a
|
|
<interfacename>UserDetailsService</interfacename> (as a DAO) in order to lookup the
|
|
username, password and <interfacename>GrantedAuthority</interfacename>s. It
|
|
authenticates the user simply by comparing the password submitted in a
|
|
<classname>UsernamePasswordAuthenticationToken</classname> against the one loaded by
|
|
the <interfacename>UserDetailsService</interfacename>. Configuring the provider is
|
|
quite simple: <programlisting language="xml"><![CDATA[
|
|
<bean id="daoAuthenticationProvider"
|
|
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
|
|
<property name="userDetailsService" ref="inMemoryDaoImpl"/>
|
|
<property name="saltSource" ref bean="saltSource"/>
|
|
<property name="passwordEncoder" ref="passwordEncoder"/>
|
|
</bean>]]></programlisting> The <interfacename>PasswordEncoder</interfacename> and
|
|
<interfacename>SaltSource</interfacename> are optional. A
|
|
<interfacename>PasswordEncoder</interfacename> provides encoding and decoding of
|
|
passwords presented in the <interfacename>UserDetails</interfacename> object that is
|
|
returned from the configured <interfacename>UserDetailsService</interfacename>. A
|
|
<interfacename>SaltSource</interfacename> enables the passwords to be populated with
|
|
a "salt", which enhances the security of the passwords in the authentication
|
|
repository. These will be discussed in more detail <link
|
|
xlink:href="#core-services-password-encoding">below</link>. </para>
|
|
</section>
|
|
</section>
|
|
<section>
|
|
<title><interfacename>UserDetailsService</interfacename> Implementations</title>
|
|
<para>As mentioned in the earlier in this reference guide, most authentication providers
|
|
take advantage of the <interfacename>UserDetails</interfacename> and
|
|
<interfacename>UserDetailsService</interfacename> interfaces. Recall that the contract
|
|
for <interfacename>UserDetailsService</interfacename> is a single method:</para>
|
|
<para>
|
|
<programlisting>
|
|
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
|
|
</programlisting> </para>
|
|
<para>The returned <interfacename>UserDetails</interfacename> is an interface that provides
|
|
getters that guarantee non-null provision of authentication information such as the
|
|
username, password, granted authorities and whether the user account 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. They may use the returned
|
|
<interfacename>UserDetails</interfacename> object just for its
|
|
<literal>GrantedAuthority</literal> information, because some other system (like LDAP or
|
|
X.509 or CAS etc) has undertaken the responsibility of actually validating the
|
|
credentials.</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="core-services-in-memory-service">
|
|
<title>In-Memory Authentication</title>
|
|
<para>Is easy to use create a custom <interfacename>UserDetailsService</interfacename>
|
|
implementation that extracts information from a persistence engine of choice, but
|
|
many applications do not require such complexity. This is particularly true if
|
|
you're building a prototype application 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="#ns-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 supports 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="core-services-jdbc-user-service">
|
|
<title><literal>JdbcDaoImpl</literal></title>
|
|
<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 language="xml"><![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 with any other Spring configuration.</para>
|
|
<section>
|
|
<title>Authority Groups</title>
|
|
<para>By default, <classname>JdbcDaoImpl</classname> loads the authorities for a
|
|
single user with the assumption that the authorities are mapped directly to
|
|
users (see the <link xlink:href="#appendix-schema">database schema
|
|
appendix</link>). An alternative approach is to partition the authorities into
|
|
groups and assign groups to the user. Some people prefer this approach as a
|
|
means of administering user rights. See the <classname>JdbcDaoImpl</classname>
|
|
Javadoc for more information on how to enable the use of group authorities. The
|
|
group schema is also included in the appendix.</para>
|
|
</section>
|
|
<!--
|
|
<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 xml:id="core-services-password-encoding">
|
|
<title>Password Encoding</title>
|
|
<para>Spring Security's <interfacename>PasswordEncoder</interfacename> interface is used to
|
|
support the use of passwords which are encoded in some way in persistent storage. This
|
|
will normally mean that the passwords are <quote>hashed</quote> using a digest algorithm
|
|
such as MD5 or SHA.</para>
|
|
<section>
|
|
<title>What is a hash?</title>
|
|
<para>Password hashing is not unique to Spring Security but is a common source of
|
|
confusion for users who are not familiar with the concept. A hash (or digest)
|
|
algorithm is a one-way function which produces a piece of fixed-length output data
|
|
(the hash) from some input data, such as a password. As an example, the MD5 hash of
|
|
the string <quote>password</quote> (in hexadecimal) is
|
|
<programlisting>
|
|
5f4dcc3b5aa765d61d8327deb882cf99
|
|
</programlisting> A hash is
|
|
<quote>one-way</quote> in the sense that it is very difficult (effectively
|
|
impossible) to obtain the original input given the hash value, or indeed any
|
|
possible input which would produce that hash value. This property makes hash values
|
|
very useful for authentication purposes. They can be stored in your user database as
|
|
an alternative to plaintext passwords and even if the values are compromised they do
|
|
not immediately reveal a password which can be used to login. Note that this also
|
|
means you have no way of recovering the password once it is encoded.</para>
|
|
</section>
|
|
<section>
|
|
<title>Adding Salt to a Hash</title>
|
|
<para> One potential problem with the use of password hashes that it is relatively easy
|
|
to get round the one-way property of the hash if a common word is used for the
|
|
input. For example, if you search for the hash value
|
|
<literal>5f4dcc3b5aa765d61d8327deb882cf99</literal> using google, you will quickly
|
|
find the original word <quote>password</quote>. In a similar way, an attacker can
|
|
build a dictionary of hashes from a standard word list and use this to lookup the
|
|
original password. One way to help prevent this is to have a suitably strong
|
|
password policy to try to prevent common words from being used. Another is to use a
|
|
<quote>salt</quote> when calculating the hashes. This is an additional string of
|
|
known data for each user which is combined with the password before calculating the
|
|
hash. Ideally the data should be as random as possible, but in practice any salt
|
|
value is usually preferable to none. Spring Security has a
|
|
<interfacename>SaltSource</interfacename> interface which can be used by an
|
|
authentication provider to generate a salt value for a particular user. Using a salt
|
|
means that an attacker has to build a separate dictionary of hashes for each salt
|
|
value, making the attack more complicated (but not impossible).</para>
|
|
</section>
|
|
<section>
|
|
<title> Hashing and Authentication</title>
|
|
<para>When an authentication provider (such as Spring Security's
|
|
<classname>DaoAuthenticationProvider</classname> needs to check the password in a
|
|
submitted authentication request against the known value for a user, and the stored
|
|
password is encoded in some way, then the submitted value must be encoded using
|
|
exactly the same algorithm. It's up to you to check that these are compatible as
|
|
Spring Security has no control over the persistent values. If you add password
|
|
hashing to your authentication configuration in Spring Security, and your database
|
|
contains plaintext passwords, then there is no way authentication can succeed. Even
|
|
if you are aware that your database is using MD5 to encode the passwords, for
|
|
example, and your application is configured to use Spring Security's
|
|
<classname>Md5PasswordEncoder</classname>, there are still things that can go wrong.
|
|
The database may have the passwords encoded in Base 64, for example while the
|
|
enocoder is using hexadecimal strings (the default)<footnote>
|
|
<para>You can configure the encoder to use Base 64 instead of hex by setting the
|
|
<literal>encodeHashAsBase64</literal> property to <literal>true</literal>. Check
|
|
the Javadoc for <classname>MessageDigestPasswordEncoder</classname> and its
|
|
parent classes for more information.</para>
|
|
</footnote>. Alternatively your database may be using upper-case while the output
|
|
from the encoder is lower-case. Make sure you write a test to check the output from
|
|
your configured password encoder with a known password and salt combination and
|
|
check that it matches the database value before going further and attempting to
|
|
authenticate through your application. For more information on the default method
|
|
for merging salt and password, see the Javadoc for
|
|
<classname>BasePasswordEncoder</classname>. If you want to generate encoded
|
|
passwords directly in Java for storage in your user database, then you can use the
|
|
<methodname>encodePassword</methodname> method on the
|
|
<interfacename>PasswordEncoder</interfacename>.</para>
|
|
</section>
|
|
</section>
|
|
</chapter>
|