parent
4152530e69
commit
cf4272ff64
|
@ -0,0 +1,178 @@
|
|||
|
||||
[[core-services]]
|
||||
== Core Services
|
||||
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 `AuthenticationManager`, `UserDetailsService` and the `AccessDecisionManager`.
|
||||
These crop up regularly throughout the remainder of this document so it's important you know how they are configured and how they operate.
|
||||
|
||||
|
||||
[[core-services-authentication-manager]]
|
||||
=== 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`.
|
||||
|
||||
If you are using the namespace, an instance of `ProviderManager` is created and maintained internally, and you add providers to it by using the namespace authentication provider elements (see <<ns-auth-manager,the namespace chapter>>).
|
||||
In this case, you should not declare a `ProviderManager` bean in your application context.
|
||||
However, if you are not using the namespace then you would declare it like so:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
|
||||
<bean id="authenticationManager"
|
||||
class="org.springframework.security.authentication.ProviderManager">
|
||||
<constructor-arg>
|
||||
<list>
|
||||
<ref local="daoAuthenticationProvider"/>
|
||||
<ref local="anonymousAuthenticationProvider"/>
|
||||
<ref local="ldapAuthenticationProvider"/>
|
||||
</list>
|
||||
</constructor-arg>
|
||||
</bean>
|
||||
----
|
||||
|
||||
In the above example we have three providers.
|
||||
They are tried in the order shown (which is implied by the use of a `List`), with each provider able to attempt authentication, or skip authentication by simply returning `null`.
|
||||
If all implementations return null, the `ProviderManager` will throw a `ProviderNotFoundException`.
|
||||
If you're interested in learning more about chaining providers, please refer to the `ProviderManager` Javadoc.
|
||||
|
||||
Authentication mechanisms such as a web form-login processing filter are injected with a reference to the `ProviderManager` 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, `DaoAuthenticationProvider` and `LdapAuthenticationProvider` 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 `AuthenticationProvider`.
|
||||
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 `CasAuthenticationProvider`.
|
||||
You needn't be too concerned about this, because if you forget to register a suitable provider, you'll simply receive a `ProviderNotFoundException` when an attempt to authenticate is made.
|
||||
|
||||
|
||||
[[core-services-erasing-credentials]]
|
||||
==== Erasing Credentials on Successful Authentication
|
||||
By default (from Spring Security 3.1 onwards) the `ProviderManager` will attempt to clear any sensitive credentials information from the `Authentication` object which is returned by a successful authentication request.
|
||||
This prevents information like passwords being retained longer than necessary.
|
||||
|
||||
This may cause issues when you are using a cache of user objects, for example, to improve performance in a stateless application.
|
||||
If the `Authentication` contains a reference to an object in the cache (such as a `UserDetails` 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 `AuthenticationProvider` which creates the returned `Authentication` object.
|
||||
Alternatively, you can disable the `eraseCredentialsAfterAuthentication` property on `ProviderManager`.
|
||||
See the Javadoc for more information.
|
||||
|
||||
|
||||
[[core-services-dao-provider]]
|
||||
==== 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:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
|
||||
<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`.
|
||||
This will be discussed in more detail <<core-services-password-encoding,below>>.
|
||||
|
||||
|
||||
=== 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:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
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.
|
||||
|
||||
|
||||
[[core-services-in-memory-service]]
|
||||
==== 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 <<ns-minimal,namespace>>:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<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>
|
||||
----
|
||||
|
||||
|
||||
This also supports the use of an external properties file:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<user-service id="userDetailsService" properties="users.properties"/>
|
||||
----
|
||||
|
||||
The properties file should contain entries in the form
|
||||
|
||||
[source,txt]
|
||||
----
|
||||
username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]
|
||||
----
|
||||
|
||||
For example
|
||||
|
||||
[source,txt]
|
||||
----
|
||||
jimi=jimispassword,ROLE_USER,ROLE_ADMIN,enabled
|
||||
bob=bobspassword,ROLE_USER,enabled
|
||||
----
|
||||
|
||||
[[core-services-jdbc-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:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<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>
|
||||
----
|
||||
|
||||
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 with any other Spring configuration.
|
||||
|
||||
===== Authority Groups
|
||||
By default, `JdbcDaoImpl` loads the authorities for a single user with the assumption that the authorities are mapped directly to users (see the <<appendix-schema,database schema appendix>>).
|
||||
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 `JdbcDaoImpl` Javadoc for more information on how to enable the use of group authorities.
|
||||
The group schema is also included in the appendix.
|
||||
|
||||
include::password-encoder.adoc[leveloffset=+2]
|
||||
|
||||
include::jackson.adoc[]
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
[[overall-architecture]]
|
||||
= Architecture and Implementation
|
||||
Once you are familiar with setting up and running some namespace-configuration based applications, you may wish to develop more of an understanding of how the framework actually works behind the namespace facade.
|
||||
Like most software, Spring Security has certain central interfaces, classes and conceptual abstractions that are commonly used throughout the framework.
|
||||
In this part of the reference guide we will look at some of these and see how they work together to support authentication and access-control within Spring Security.
|
||||
|
||||
include::technical-overview.adoc[]
|
||||
|
||||
include::core-services.adoc[]
|
|
@ -1,9 +1,3 @@
|
|||
[[overall-architecture]]
|
||||
= Architecture and Implementation
|
||||
Once you are familiar with setting up and running some namespace-configuration based applications, you may wish to develop more of an understanding of how the framework actually works behind the namespace facade.
|
||||
Like most software, Spring Security has certain central interfaces, classes and conceptual abstractions that are commonly used throughout the framework.
|
||||
In this part of the reference guide we will look at some of these and see how they work together to support authentication and access-control within Spring Security.
|
||||
|
||||
|
||||
[[technical-overview]]
|
||||
== Technical Overview
|
||||
|
@ -466,182 +460,3 @@ You can either do this in a filter yourself (which must come before the Spring S
|
|||
Please refer to the Spring Framework documentation for further details on using localization with Spring.
|
||||
|
||||
The "contacts" sample application is set up to use localized messages.
|
||||
|
||||
|
||||
[[core-services]]
|
||||
== Core Services
|
||||
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 `AuthenticationManager`, `UserDetailsService` and the `AccessDecisionManager`.
|
||||
These crop up regularly throughout the remainder of this document so it's important you know how they are configured and how they operate.
|
||||
|
||||
|
||||
[[core-services-authentication-manager]]
|
||||
=== 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`.
|
||||
|
||||
If you are using the namespace, an instance of `ProviderManager` is created and maintained internally, and you add providers to it by using the namespace authentication provider elements (see <<ns-auth-manager,the namespace chapter>>).
|
||||
In this case, you should not declare a `ProviderManager` bean in your application context.
|
||||
However, if you are not using the namespace then you would declare it like so:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
|
||||
<bean id="authenticationManager"
|
||||
class="org.springframework.security.authentication.ProviderManager">
|
||||
<constructor-arg>
|
||||
<list>
|
||||
<ref local="daoAuthenticationProvider"/>
|
||||
<ref local="anonymousAuthenticationProvider"/>
|
||||
<ref local="ldapAuthenticationProvider"/>
|
||||
</list>
|
||||
</constructor-arg>
|
||||
</bean>
|
||||
----
|
||||
|
||||
In the above example we have three providers.
|
||||
They are tried in the order shown (which is implied by the use of a `List`), with each provider able to attempt authentication, or skip authentication by simply returning `null`.
|
||||
If all implementations return null, the `ProviderManager` will throw a `ProviderNotFoundException`.
|
||||
If you're interested in learning more about chaining providers, please refer to the `ProviderManager` Javadoc.
|
||||
|
||||
Authentication mechanisms such as a web form-login processing filter are injected with a reference to the `ProviderManager` 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, `DaoAuthenticationProvider` and `LdapAuthenticationProvider` 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 `AuthenticationProvider`.
|
||||
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 `CasAuthenticationProvider`.
|
||||
You needn't be too concerned about this, because if you forget to register a suitable provider, you'll simply receive a `ProviderNotFoundException` when an attempt to authenticate is made.
|
||||
|
||||
|
||||
[[core-services-erasing-credentials]]
|
||||
==== Erasing Credentials on Successful Authentication
|
||||
By default (from Spring Security 3.1 onwards) the `ProviderManager` will attempt to clear any sensitive credentials information from the `Authentication` object which is returned by a successful authentication request.
|
||||
This prevents information like passwords being retained longer than necessary.
|
||||
|
||||
This may cause issues when you are using a cache of user objects, for example, to improve performance in a stateless application.
|
||||
If the `Authentication` contains a reference to an object in the cache (such as a `UserDetails` 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 `AuthenticationProvider` which creates the returned `Authentication` object.
|
||||
Alternatively, you can disable the `eraseCredentialsAfterAuthentication` property on `ProviderManager`.
|
||||
See the Javadoc for more information.
|
||||
|
||||
|
||||
[[core-services-dao-provider]]
|
||||
==== 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:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
|
||||
<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`.
|
||||
This will be discussed in more detail <<core-services-password-encoding,below>>.
|
||||
|
||||
|
||||
=== 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:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
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.
|
||||
|
||||
|
||||
[[core-services-in-memory-service]]
|
||||
==== 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 <<ns-minimal,namespace>>:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<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>
|
||||
----
|
||||
|
||||
|
||||
This also supports the use of an external properties file:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<user-service id="userDetailsService" properties="users.properties"/>
|
||||
----
|
||||
|
||||
The properties file should contain entries in the form
|
||||
|
||||
[source,txt]
|
||||
----
|
||||
username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]
|
||||
----
|
||||
|
||||
For example
|
||||
|
||||
[source,txt]
|
||||
----
|
||||
jimi=jimispassword,ROLE_USER,ROLE_ADMIN,enabled
|
||||
bob=bobspassword,ROLE_USER,enabled
|
||||
----
|
||||
|
||||
[[core-services-jdbc-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:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<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>
|
||||
----
|
||||
|
||||
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 with any other Spring configuration.
|
||||
|
||||
===== Authority Groups
|
||||
By default, `JdbcDaoImpl` loads the authorities for a single user with the assumption that the authorities are mapped directly to users (see the <<appendix-schema,database schema appendix>>).
|
||||
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 `JdbcDaoImpl` Javadoc for more information on how to enable the use of group authorities.
|
||||
The group schema is also included in the appendix.
|
||||
|
||||
include::password-encoder.adoc[leveloffset=+2]
|
||||
|
||||
include::jackson.adoc[]
|
||||
|
|
@ -8,7 +8,7 @@ It is the de-facto standard for securing Spring-based applications.
|
|||
|
||||
include::{include-dir}/preface/index.adoc[]
|
||||
|
||||
include::{include-dir}/architecture.adoc[]
|
||||
include::{include-dir}/architecture/index.adoc[]
|
||||
|
||||
include::{include-dir}/test.adoc[]
|
||||
|
||||
|
|
Loading…
Reference in New Issue