Database Authentication
Spring's Security DaoAuthenticationProvider
is a simple authentication provider that uses a Data Access Object (DAO) to retrieve user information from a relational database. It leverages a UserDetailsService
(as a DAO) in order to lookup the username, password and GrantedAuthority
s. It authenticates the user simply by comparing the password submitted in a UsernamePasswordAuthenticationToken
against the one loaded by the UserDetailsService
.
Configuring the provider is quite simple:
<bean id="daoAuthenticationProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="daoUserDetailsService" />
</bean>
In addition, you can optionally configure a PasswordEncoder
and a SaltSource
. A PasswordEncoder
provides encoding and decoding of passwords presented in the UserDetails
object that is returned from the configured UserDetailsService
. A SaltSource
enables the passwords to be populated with a "salt", which enhances the security of the passwords in the authentication repository. For more details, refer to Spring Security reference.
Using an In-Memory DAO
Spring Security comes with an implementation of UserDetailsService that draws its user information from its Spring configuration. This is perfect when just starting to integrate Spring Security. Here's a sample configuration:
<sec:user-service>
<sec:user name="Edward" password="koala" authorities="SpacePrivilege READ ClassFilter eg.cinema.Movie, SpacePrivilege READ ClassFilter eg.cinema.Seat, SpacePrivilege WRITE ClassFilter eg.cinema.Seat" />
<sec:user name="Davis" password="dingo" authorities="SpacePrivilege READ ClassFilter eg.cinema.Movie, SpacePrivilege READ ClassFilter eg.cinema.Seat, SpacePrivilege WRITE ClassFilter eg.cinema.Seat" />
<sec:user name="Thomas" password="wombat" authorities="SpacePrivilege READ ClassFilter eg.cinema.Movie, SpacePrivilege READ ClassFilter eg.cinema.Seat, SpacePrivilege WRITE ClassFilter eg.cinema.Seat, SpacePrivilege WRITE ClassFilter eg.cinema.Movie, SpacePrivilege TAKE ClassFilter eg.cinema.Movie" />
<sec:user name="Allen" password="kangaroo" authorities="GridPrivilege MANAGE_GRID, GridPrivilege MANAGE_PU, GridPrivilege PROVISION_PU, SpacePrivilege READ PackageFilter eg.cinema" />
</sec:user-service>
Consider Edward, a Box-Office Employee, which has privileges to list all movies and their available seats, and to reserve a seat. Edward is granted READ
privileges for class eg.cinema.Movie
and for class eg.cinema.Seat
, and WRITE
privileges to update a eg.cinema.Seat
as reserved. Of course, this can get quite cumbersome for production use.
This Spring Security configuration file can be found under $GS_HOME/config/security/in-memory-security-config.xml
.
Declaring a JDBC DAO
Spring Security also includes JdbcDaoImpl
, a UserDetailsService
that can obtain authentication information from a JDBC data source. It can be declared in the Spring configuration file as follows:
<bean id="daoUserDetailsService" class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
<property name="dataSource" ref="jdbcDataSource" />
</bean>
<bean id="jdbcDataSource"
class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
<property name="driverClass" value="org.hsqldb.jdbcDriver" />
<property name="url" value="jdbc:hsqldb:hsql://localhost:9001" />
</bean>
There are some basic assumptions on how user information is stored in the database. Specifically, it assumes a Users table and an Authorities table. By default, JdbcDaoImpl
loads the authorities for a single user with the assumption that the authorities are mapped directly to users. An alternative approach is to partition the authorities into groups (roles) and assign groups to the user. For more information, refer to the JdbcDaoImpl
Javadoc and the Security Database Schema Appendix.
Here is a snippet:
create table users(
username varchar_ignorecase(50) not null primary key,
password varchar_ignorecase(50) not null,
enabled boolean not null);
create table authorities (
username varchar_ignorecase(50) not null,
authority varchar_ignorecase(50) not null,
constraint fk_authorities_users foreign key(username) references users(username));
create unique index ix_auth_username on authorities (username,authority);
---
Group Authorities (if enabled)
create table groups (
id bigint generated by default as identity(start with 0) primary key,
group_name varchar_ignorecase(50) not null);
create table group_authorities (
group_id bigint not null,
authority varchar(50) not null,
constraint fk_group_authorities_group foreign key(group_id) references groups(id));
create table group_members (
id bigint generated by default as identity(start with 0) primary key,
username varchar(50) not null,
group_id bigint not null,
constraint fk_group_members_group foreign key(group_id) references groups(id));
The illustration below represents the table structures assumed by JdbcDaoImpl
and an example table data holding our "Box Office" users and roles. If you are not using groups (roles) then a users
table and an authorities
table will do.
Edward, Arthur, and Thomas are all "Box Office employees" that share this common role, with privileges to list all movies and their available seats, and to reserve a seat. On the other hand, Thomas is also a "Box Office Manager", with privileges to set up a new movie and remove old movies. Emily is a "Box Office Administrator" who is responsible for setting up the "Box Office" application, and also has (non-role) privileges to read and write all data related to the cinema.
The database tables assumed by JdbcDaoImpl
Box-Office users and roles
To use JdbcDaoImpl
, you might need to configure it to find the user information in regards to your database schema (assuming it is different from the default). By setting the usersByUsernameQuery
, authoritiesByUsernameQuery
, and groupAuthoritiesByUsernameQuery
you can configure the JdbcDaoImpl
to retrieve the user information and granted authorities based on your database schema.
When JdbcDaoImpl
looks up user information, it will query with the following SQL:
SELECT username,password,enabled FROM users WHERE username = ?
You may have noticed that we omitted the "enabled' column in our example Users table. For this to work, we need to change the usersByUsernameQuery
to return "true' for the "enabled' column value. Likewise, you would set the query to suite your needs.
<bean id="daoUserDetailsService" class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
<property name="dataSource" ref="jdbcDataSource" />
<property name="usersByUsernameQuery">
<value>SELECT username,password,'true' FROM users WHERE username = ?</value>
</property>
...
</bean>
This Spring Security configuration file can be found under $GS_HOME/config/security/jdbc-security-config.xml
.
Working with Encrypted Passwords
Spring Security's PasswordEncoder
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 "hashed" using a digest algorithm such as MD5 or SHA.
Here is how to wire DaoAuthenticationProvider
to use MD5 encoding:
<bean id="daoAuthenticationProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="daoUserDetailsService" />
<property name="passwordEncoder">
<bean class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />
</property>
</bean>
You can also set a salt source for the encoder. For more information, refer to Spring Security reference.
Summary
By now you should be familiar with some important concepts:
-
Setting up a Spring-based security bridge with a simple in-memory DAO or a relational database,
-
Defining privileges for a user, configure a password encoder,
-
Test a standalone security bridge against your configuration,
-
Launch a simple GigaSpaces instance with a Spring Security back-end.