1. The AuthenticationManager, ProviderManager and AuthenticationProvider
The AuthenticationManager
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?
The default implementation in Spring Security is called ProviderManager
and rather than handling the authentication request itself, it delegates to a list of configured AuthenticationProvider
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 Authentication
object. Remember our good friends, UserDetails
and UserDetailsService
? 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 UserDetails
and check the loaded password against the one that has been entered by the user. This is the approach used by the DaoAuthenticationProvider
(see below). The loaded UserDetails
object - and particularly the GrantedAuthority
s it contains - will be used when building the fully populated Authentication
object which is returned from a successful authentication and stored in the SecurityContext
.
DaoAuthenticationProvider
The simplest AuthenticationProvider
implemented by Spring Security is DaoAuthenticationProvider
, which is also one of the earliest supported by the framework. 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="inMemoryDaoImpl"/>
<property name="passwordEncoder" ref="passwordEncoder"/>
</bean>
The PasswordEncoder
is optional. A PasswordEncoder
provides encoding and decoding of passwords presented in the UserDetails
object that is returned from the configured UserDetailsService
.
2. UserDetailsService Implementations
As mentioned in the earlier in this reference guide, most authentication providers take advantage of the UserDetails
and UserDetailsService
interfaces. Recall that the contract for UserDetailsService
is a single method:
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
The returned UserDetails
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 UserDetailsService
, even if the username and password are not actually used as part of the authentication decision. They may use the returned UserDetails
object just for its GrantedAuthority
information, because some other system (like LDAP or X.509 or CAS etc) has undertaken the responsibility of actually validating the credentials.
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.
In-Memory Authentication
Is easy to use create a custom UserDetailsService
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 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">
<!-- Password is prefixed with {noop} to indicate to DelegatingPasswordEncoder that
NoOpPasswordEncoder should be used. This is not safe for production, but makes reading
in samples easier. Normally passwords should be hashed using BCrypt -->
<user name="jimi" password="{noop}jimispassword" authorities="ROLE_USER, ROLE_ADMIN" />
<user name="bob" password="{noop}bobspassword" authorities="ROLE_USER" />
</user-service>
JdbcDaoImpl
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.core.userdetails.jdbc.JdbcDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>