spring-security/docs/modules/ROOT/pages/servlet/authentication/architecture.adoc

262 lines
19 KiB
Plaintext

[[servlet-authentication-architecture]]
= Servlet Authentication Architecture
:figures: servlet/authentication/architecture
This discussion expands on xref:servlet/architecture.adoc#servlet-architecture[Servlet Security: The Big Picture] to describe the main architectural components of Spring Security's used in Servlet authentication.
If you need concrete flows that explain how these pieces fit together, look at the xref:servlet/authentication/index.adoc#servlet-authentication-mechanisms[Authentication Mechanism] specific sections.
* <<servlet-authentication-securitycontextholder>> - The `SecurityContextHolder` is where Spring Security stores the details of who is xref:features/authentication/index.adoc#authentication[authenticated].
* <<servlet-authentication-securitycontext>> - is obtained from the `SecurityContextHolder` and contains the `Authentication` of the currently authenticated user.
* <<servlet-authentication-authentication>> - Can be the input to `AuthenticationManager` to provide the credentials a user has provided to authenticate or the current user from the `SecurityContext`.
* <<servlet-authentication-granted-authority>> - An authority that is granted to the principal on the `Authentication` (i.e. roles, scopes, etc.)
* <<servlet-authentication-authenticationmanager>> - the API that defines how Spring Security's Filters perform xref:features/authentication/index.adoc#authentication[authentication].
* <<servlet-authentication-providermanager>> - the most common implementation of `AuthenticationManager`.
* <<servlet-authentication-authenticationprovider>> - used by `ProviderManager` to perform a specific type of authentication.
* <<servlet-authentication-authenticationentrypoint>> - used for requesting credentials from a client (i.e. redirecting to a log in page, sending a `WWW-Authenticate` response, etc.)
* <<servlet-authentication-abstractprocessingfilter>> - a base `Filter` used for authentication.
This also gives a good idea of the high level flow of authentication and how pieces work together.
[[servlet-authentication-securitycontextholder]]
== SecurityContextHolder
At the heart of Spring Security's authentication model is the `SecurityContextHolder`.
It contains the <<servlet-authentication-securitycontext>>.
image::{figures}/securitycontextholder.png[]
The `SecurityContextHolder` is where Spring Security stores the details of who is xref:features/authentication/index.adoc#authentication[authenticated].
Spring Security does not care how the `SecurityContextHolder` is populated.
If it contains a value, it is used as the currently authenticated user.
The simplest way to indicate a user is authenticated is to set the `SecurityContextHolder` directly:
.Setting `SecurityContextHolder`
====
.Java
[source,java,role="primary"]
----
SecurityContext context = SecurityContextHolder.createEmptyContext(); // <1>
Authentication authentication =
new TestingAuthenticationToken("username", "password", "ROLE_USER"); // <2>
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context); // <3>
----
.Kotlin
[source,kotlin,role="secondary"]
----
val context: SecurityContext = SecurityContextHolder.createEmptyContext() // <1>
val authentication: Authentication = TestingAuthenticationToken("username", "password", "ROLE_USER") // <2>
context.authentication = authentication
SecurityContextHolder.setContext(context) // <3>
----
<1> We start by creating an empty `SecurityContext`.
You should create a new `SecurityContext` instance instead of using `SecurityContextHolder.getContext().setAuthentication(authentication)` to avoid race conditions across multiple threads.
<2> Next, we create a new <<servlet-authentication-authentication,`Authentication`>> object.
Spring Security does not care what type of `Authentication` implementation is set on the `SecurityContext`.
Here, we use `TestingAuthenticationToken`, because it is very simple.
A more common production scenario is `UsernamePasswordAuthenticationToken(userDetails, password, authorities)`.
<3> Finally, we set the `SecurityContext` on the `SecurityContextHolder`.
Spring Security uses this information for xref:servlet/authorization/index.adoc#servlet-authorization[authorization].
====
To obtain information about the authenticated principal, access the `SecurityContextHolder`.
.Access Currently Authenticated User
====
.Java
[source,java,role="primary"]
----
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
String username = authentication.getName();
Object principal = authentication.getPrincipal();
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
----
.Kotlin
[source,kotlin,role="secondary"]
----
val context = SecurityContextHolder.getContext()
val authentication = context.authentication
val username = authentication.name
val principal = authentication.principal
val authorities = authentication.authorities
----
====
// FIXME: Add links to and relevant description of HttpServletRequest.getRemoteUser() and @CurrentSecurityContext @AuthenticationPrincipal
By default, `SecurityContextHolder` uses a `ThreadLocal` to store these details, which means that the `SecurityContext` is always available to methods in the same thread, even if the `SecurityContext` is not explicitly passed around as an argument to those methods.
Using a `ThreadLocal` in this way is quite safe if you take care to clear the thread after the present principal's request is processed.
Spring Security's xref:servlet/architecture.adoc#servlet-filterchainproxy[FilterChainProxy] ensures that the `SecurityContext` is always cleared.
Some applications are not entirely suitable for using a `ThreadLocal`, because of the specific way they work with threads.
For example, a Swing client might want all threads in a Java Virtual Machine to use the same security context.
You can configure `SecurityContextHolder` with a strategy on startup to specify how you would like the context to be stored.
For a standalone application, you would use the `SecurityContextHolder.MODE_GLOBAL` strategy.
Other applications might want to have threads spawned by the secure thread also assume the same security identity.
You can achieve this by using `SecurityContextHolder.MODE_INHERITABLETHREADLOCAL`.
You can change the mode from the default `SecurityContextHolder.MODE_THREADLOCAL` in two ways.
The first is to set a system property.
The second is to call a static method on `SecurityContextHolder`.
Most applications need not change from the default.
However, if you do, take a look at the JavaDoc for `SecurityContextHolder` to learn more.
[[servlet-authentication-securitycontext]]
== SecurityContext
The {security-api-url}org/springframework/security/core/context/SecurityContext.html[`SecurityContext`] is obtained from the <<servlet-authentication-securitycontextholder>>.
The `SecurityContext` contains an <<servlet-authentication-authentication>> object.
[[servlet-authentication-authentication]]
== Authentication
The {security-api-url}org/springframework/security/core/Authentication.html[`Authentication`] interface serves two main purposes within Spring Security:
* An input to <<servlet-authentication-authenticationmanager,`AuthenticationManager`>> to provide the credentials a user has provided to authenticate.
When used in this scenario, `isAuthenticated()` returns `false`.
* Represent the currently authenticated user.
You can obtain the current `Authentication` from the <<servlet-authentication-securitycontext>>.
The `Authentication` contains:
* `principal`: Identifies the user.
When authenticating with a username/password this is often an instance of xref:servlet/authentication/passwords/user-details.adoc#servlet-authentication-userdetails[`UserDetails`].
* `credentials`: Often a password.
In many cases, this is cleared after the user is authenticated, to ensure that it is not leaked.
* `authorities`: The <<servlet-authentication-granted-authority,`GrantedAuthority`>> instances are high-level permissions the user is granted.
Two examples are roles and scopes.
[[servlet-authentication-granted-authority]]
== GrantedAuthority
{security-api-url}org/springframework/security/core/GrantedAuthority.html[`GrantedAuthority`] instances are high-level permissions that the user is granted.
Two examples are roles and scopes.
You can obtain `GrantedAuthority` instances from the <<servlet-authentication-authentication,`Authentication.getAuthorities()`>> method.
This method provides a `Collection` of `GrantedAuthority` objects.
A `GrantedAuthority` is, not surprisingly, an authority that is granted to the principal.
Such authorities are usually "`roles`", such as `ROLE_ADMINISTRATOR` or `ROLE_HR_SUPERVISOR`.
These roles are later configured for web authorization, method authorization, and domain object authorization.
Other parts of Spring Security interpret these authorities and expect them to be present.
When using username/password based authentication `GrantedAuthority` instances are usually loaded by the xref:servlet/authentication/passwords/user-details-service.adoc#servlet-authentication-userdetailsservice[`UserDetailsService`].
Usually, the `GrantedAuthority` objects are application-wide permissions.
They are not specific to a given domain object.
Thus, you would not likely have a `GrantedAuthority` to represent a permission to `Employee` object number 54, because if there are thousands of such authorities you would quickly run out of memory (or, at the very least, cause the application to take a long time to authenticate a user).
Of course, Spring Security is expressly designed to handle this common requirement, but you should instead use the project's domain object security capabilities for this purpose.
[[servlet-authentication-authenticationmanager]]
== AuthenticationManager
{security-api-url}org/springframework/security/authentication/AuthenticationManager.html[`AuthenticationManager`] is the API that defines how Spring Security's Filters perform xref:features/authentication/index.adoc#authentication[authentication].
The <<servlet-authentication-authentication,`Authentication`>> that is returned is then set on the <<servlet-authentication-securitycontextholder>> by the controller (that is, by <<servlet-security-filters,Spring Security's `Filters` instances>>) that invoked the `AuthenticationManager`.
If you are not integrating with Spring Security's `Filters` instances, you can set the `SecurityContextHolder` directly and are not required to use an `AuthenticationManager`.
While the implementation of `AuthenticationManager` could be anything, the most common implementation is <<servlet-authentication-providermanager,`ProviderManager`>>.
// FIXME: add configuration
[[servlet-authentication-providermanager]]
== ProviderManager
{security-api-url}org/springframework/security/authentication/ProviderManager.html[`ProviderManager`] is the most commonly used implementation of <<servlet-authentication-authenticationmanager,`AuthenticationManager`>>.
`ProviderManager` delegates to a `List` of <<servlet-authentication-authenticationprovider,`AuthenticationProvider`>> instances.
Each `AuthenticationProvider` has an opportunity to indicate that authentication should be successful, fail, or indicate it cannot make a decision and allow a downstream `AuthenticationProvider` to decide.
If none of the configured `AuthenticationProvider` instances can authenticate, authentication fails with a `ProviderNotFoundException`, which is a special `AuthenticationException` that indicates that the `ProviderManager` was not configured to support the type of `Authentication` that was passed into it.
image::{figures}/providermanager.png[]
In practice each `AuthenticationProvider` knows how to perform a specific type of authentication.
For example, one `AuthenticationProvider` might be able to validate a username/password, while another might be able to authenticate a SAML assertion.
This lets each `AuthenticationProvider` do a very specific type of authentication while supporting multiple types of authentication and expose only a single `AuthenticationManager` bean.
`ProviderManager` also allows configuring an optional parent `AuthenticationManager`, which is consulted in the event that no `AuthenticationProvider` can perform authentication.
The parent can be any type of `AuthenticationManager`, but it is often an instance of `ProviderManager`.
image::{figures}/providermanager-parent.png[]
In fact, multiple `ProviderManager` instances might share the same parent `AuthenticationManager`.
This is somewhat common in scenarios where there are multiple xref:servlet/architecture.adoc#servlet-securityfilterchain[`SecurityFilterChain`] instances that have some authentication in common (the shared parent `AuthenticationManager`), but also different authentication mechanisms (the different `ProviderManager` instances).
image::{figures}/providermanagers-parent.png[]
[[servlet-authentication-providermanager-erasing-credentials]]
By default, `ProviderManager` tries to clear any sensitive credentials information from the `Authentication` object that is returned by a successful authentication request.
This prevents information, such as passwords, being retained longer than necessary in the `HttpSession`.
This may cause issues when you use 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, it is no longer possible to authenticate against the cached value.
You need to take this into account if you use a cache.
An obvious solution is to first make a copy of the object, either in the cache implementation or in the `AuthenticationProvider` that creates the returned `Authentication` object.
Alternatively, you can disable the `eraseCredentialsAfterAuthentication` property on `ProviderManager`.
See the Javadoc for the {security-api-url}org/springframework/security/authentication/ProviderManager.html[Javadoc] class.
[[servlet-authentication-authenticationprovider]]
== AuthenticationProvider
You can inject multiple {security-api-url}org/springframework/security/authentication/AuthenticationProvider.html[``AuthenticationProvider``s] instances into <<servlet-authentication-providermanager,`ProviderManager`>>.
Each `AuthenticationProvider` performs a specific type of authentication.
For example, xref:servlet/authentication/passwords/dao-authentication-provider.adoc#servlet-authentication-daoauthenticationprovider[`DaoAuthenticationProvider`] supports username/password-based authentication, while `JwtAuthenticationProvider` supports authenticating a JWT token.
[[servlet-authentication-authenticationentrypoint]]
== Request Credentials with `AuthenticationEntryPoint`
{security-api-url}org/springframework/security/web/AuthenticationEntryPoint.html[`AuthenticationEntryPoint`] is used to send an HTTP response that requests credentials from a client.
Sometimes, a client proactively includes credentials (such as a username and password) to request a resource.
In these cases, Spring Security does not need to provide an HTTP response that requests credentials from the client, since they are already included.
In other cases, a client makes an unauthenticated request to a resource that they are not authorized to access.
In this case, an implementation of `AuthenticationEntryPoint` is used to request credentials from the client.
The `AuthenticationEntryPoint` implementation might perform a xref:servlet/authentication/passwords/form.adoc#servlet-authentication-form[redirect to a log in page], respond with an xref:servlet/authentication/passwords/basic.adoc#servlet-authentication-basic[WWW-Authenticate] header, or take other action.
// FIXME: authenticationsuccesshandler
// FIXME: authenticationfailurehandler
[[servlet-authentication-abstractprocessingfilter]]
== AbstractAuthenticationProcessingFilter
{security-api-url}org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.html[`AbstractAuthenticationProcessingFilter`] is used as a base `Filter` for authenticating a user's credentials.
Before the credentials can be authenticated, Spring Security typically requests the credentials by using <<servlet-authentication-authenticationentrypoint,`AuthenticationEntryPoint`>>.
Next, the `AbstractAuthenticationProcessingFilter` can authenticate any authentication requests that are submitted to it.
image::{figures}/abstractauthenticationprocessingfilter.png[]
image:{icondir}/number_1.png[] When the user submits their credentials, the `AbstractAuthenticationProcessingFilter` creates an <<servlet-authentication-authentication,`Authentication`>> from the `HttpServletRequest` to be authenticated.
The type of `Authentication` created depends on the subclass of `AbstractAuthenticationProcessingFilter`.
For example, xref:servlet/authentication/passwords/form.adoc#servlet-authentication-usernamepasswordauthenticationfilter[`UsernamePasswordAuthenticationFilter`] creates a `UsernamePasswordAuthenticationToken` from a __username__ and __password__ that are submitted in the `HttpServletRequest`.
image:{icondir}/number_2.png[] Next, the <<servlet-authentication-authentication,`Authentication`>> is passed into the <<servlet-authentication-authenticationmanager,`AuthenticationManager`>> to be authenticated.
image:{icondir}/number_3.png[] If authentication fails, then __Failure__.
* The <<servlet-authentication-securitycontextholder>> is cleared out.
* `RememberMeServices.loginFail` is invoked.
If remember me is not configured, this is a no-op.
See the {security-api-url}org/springframework/security/web/authentication/rememberme/package-frame.html[`rememberme`] package.
* `AuthenticationFailureHandler` is invoked.
See the {security-api-url}org/springframework/security/web/authentication/AuthenticationFailureHandler.html[`AuthenticationFailureHandler`] interface.
image:{icondir}/number_4.png[] If authentication is successful, then __Success__.
* `SessionAuthenticationStrategy` is notified of a new login.
See the {security-api-url}org/springframework/security/web/authentication/session/SessionAuthenticationStrategy.html[`SessionAuthenticationStrategy`] interface.
* The <<servlet-authentication-authentication>> is set on the <<servlet-authentication-securitycontextholder>>.
Later, the `SecurityContextPersistenceFilter` saves the `SecurityContext` to the `HttpSession`.
See the {security-api-url}org/springframework/security/web/context/SecurityContextPersistenceFilter.html[`SecurityContextPersistenceFilter`] class.
* `RememberMeServices.loginSuccess` is invoked.
If remember me is not configured, this is a no-op.
See the {security-api-url}org/springframework/security/web/authentication/rememberme/package-frame.html[`rememberme`] package.
* `ApplicationEventPublisher` publishes an `InteractiveAuthenticationSuccessEvent`.
* `AuthenticationSuccessHandler` is invoked.
See the {security-api-url}org/springframework/security/web/authentication/AuthenticationSuccessHandler.html[`AuthenticationSuccessHandler`] interface.
// daoauthenticationprovider (goes in username/password)