Extract architecture subsections

Issue: gh-2567
This commit is contained in:
Rob Winch 2018-03-06 09:19:17 -06:00
parent 4152530e69
commit cf4272ff64
6 changed files with 188 additions and 186 deletions

View File

@ -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[]

View File

@ -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[]

View File

@ -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[]

View File

@ -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[]