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.