Servlet Authentication Architecture
ProviderManager AuthenticationProvider GrantedAuthority Request Credentials AuthenticationEntryPoint AbstractAuthenticationProcessingFilter SecurityContext Spring Security :description: 这篇概述描述了 Spring Security 中用于处理认证的架构组件。核心是 SecurityContextHolder,其中存储了已认证用户的详细信息。Authentication 接口定义了 Spring Security 的过滤器如何执行认证,AuthenticationManager 管理认证流程。ProviderManager 是 AuthenticationManager 的最常见实现,它使用 AuthenticationProvider 列表来执行特定类型的认证。Request Credentials with AuthenticationEntryPoint 用于向客户端请求凭证,AbstractAuthenticationProcessingFilter 提供了一个基本过滤器用于认证用户凭证。这篇文章提供了认证流程的高级视图,其中涉及这些组件如何协同工作来确定用户的认证状态。
此讨论在 Servlet Security: The Big Picture 的基础上进行扩展,以描述 Spring Security 中用于 Servlet 身份验证的主要架构组件。如果你需要具体流程来解释这些部分如何配合使用,请查看 Authentication Mechanism 的特定部分。
This discussion expands on 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 Authentication Mechanism specific sections.
-
SecurityContextHolder - The
SecurityContextHolder
is where Spring Security stores the details of who is authenticated. -
SecurityContext - is obtained from the
SecurityContextHolder
and contains theAuthentication
of the currently authenticated user. -
Authentication - Can be the input to
AuthenticationManager
to provide the credentials a user has provided to authenticate or the current user from theSecurityContext
. -
GrantedAuthority - An authority that is granted to the principal on the
Authentication
(i.e. roles, scopes, etc.) -
AuthenticationManager - the API that defines how Spring Security’s Filters perform authentication.
-
ProviderManager - the most common implementation of
AuthenticationManager
. -
AuthenticationProvider - used by
ProviderManager
to perform a specific type of authentication. -
Request Credentials with
AuthenticationEntryPoint
- used for requesting credentials from a client (i.e. redirecting to a log in page, sending aWWW-Authenticate
response, etc.) -
AbstractAuthenticationProcessingFilter - a base
Filter
used for authentication. This also gives a good idea of the high level flow of authentication and how pieces work together.
SecurityContextHolder
Spring Security 认证模型的核心是 SecurityContextHolder
。它包含 SecurityContext。
At the heart of Spring Security’s authentication model is the SecurityContextHolder
.
It contains the SecurityContext.

SecurityContextHolder
是 Spring Security 存储谁 authenticated 的详细信息的位置。Spring Security 不关心如何填充 SecurityContextHolder
。如果它包含一个值,那么它将用作当前经过身份验证的用户。
The SecurityContextHolder
is where Spring Security stores the details of who is authenticated.
Spring Security does not care how the SecurityContextHolder
is populated.
If it contains a value, it is used as the currently authenticated user.
指示用户已认证的最简单方法是直接设置 SecurityContextHolder
:
The simplest way to indicate a user is authenticated is to set the SecurityContextHolder
directly:
SecurityContextHolder
-
Java
-
Kotlin
SecurityContext context = SecurityContextHolder.createEmptyContext(); (1)
Authentication authentication =
new TestingAuthenticationToken("username", "password", "ROLE_USER"); (2)
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context); (3)
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 <<`Authentication`,servlet-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 authorization. |
要获取已认证主体的相关信息,请访问 SecurityContextHolder
。
To obtain information about the authenticated principal, access the SecurityContextHolder
.
-
Java
-
Kotlin
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
String username = authentication.getName();
Object principal = authentication.getPrincipal();
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
val context = SecurityContextHolder.getContext()
val authentication = context.authentication
val username = authentication.name
val principal = authentication.principal
val authorities = authentication.authorities
默认情况下,SecurityContextHolder
使用 ThreadLocal
来存储这些详细信息,这意味着 SecurityContext
始终可供同一线程中的方法使用,即使 SecurityContext
并未明确作为参数传递给这些方法。如果你在处理当前负责人的请求后清除线程,那么以这种方式使用 ThreadLocal
是非常安全的。Spring Security 的 FilterChainProxy 确保始终清除 SecurityContext
。
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 FilterChainProxy ensures that the SecurityContext
is always cleared.
由于某些应用程序与线程的工作方式很具体,因此并不完全适合使用 ThreadLocal
。例如,Swing 客户端可能希望 Java Virtual Machine 中的所有线程使用相同的安全上下文。你可以在启动时使用 strategy 为 SecurityContextHolder
配置,以指定如何存储上下文。对于独立应用程序,你将使用 SecurityContextHolder.MODE_GLOBAL
strategy。其他应用程序可能希望由安全线程生成的线程也假定相同安全标识。你可以使用 SecurityContextHolder.MODE_INHERITABLETHREADLOCAL
实现此目的。你可以通过两种方式更改模式,即从默认 SecurityContextHolder.MODE_THREADLOCAL
更改。第一种方法是设置系统属性。第二种方法是在 SecurityContextHolder
中调用一个 static 方法。大多数应用程序不需要改变默认设置。但是,如果你有需要,请查看 SecurityContextHolder
的 JavaDoc 以了解更多信息。
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.
SecurityContext
从 SecurityContextHolder获得 {security-api-url}org/springframework/security/core/context/SecurityContext.html[SecurityContext
]。`SecurityContext`包含一个 Authentication对象。
The {security-api-url}org/springframework/security/core/context/SecurityContext.html[SecurityContext
] is obtained from the SecurityContextHolder.
The SecurityContext
contains an Authentication object.
Authentication
在 Spring 安全中,{security-api-url}org/springframework/security/core/Authentication.html[Authentication
] 接口有两个主要用途:
The {security-api-url}org/springframework/security/core/Authentication.html[Authentication
] interface serves two main purposes within Spring Security:
-
An input to <<`AuthenticationManager`,servlet-authentication-authenticationmanager>> to provide the credentials a user has provided to authenticate. When used in this scenario,
isAuthenticated()
returnsfalse
. -
Represent the currently authenticated user. You can obtain the current
Authentication
from the SecurityContext.
Authentication
包含:
The Authentication
contains:
-
principal
: Identifies the user. When authenticating with a username/password this is often an instance ofUserDetails
. -
credentials
: Often a password. In many cases, this is cleared after the user is authenticated, to ensure that it is not leaked. -
authorities
: The <<`GrantedAuthority`,servlet-authentication-granted-authority>> instances are high-level permissions the user is granted. Two examples are roles and scopes.
GrantedAuthority
{security-api-url}org/springframework/security/core/GrantedAuthority.html[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.
你可以从 <<`Authentication.getAuthorities(),servlet-authentication-authentication>> method.
This method provides a `Collection
of GrantedAuthority
objects>> 中获取 GrantedAuthority
实例。GrantedAuthority
不出所料,它是一项授予负责人的权限。此类权限通常是 “roles”,例如 ROLE_ADMINISTRATOR
或 ROLE_HR_SUPERVISOR
。这些角色稍后将配置用于 Web 授权、方法授权和域对象授权。Spring Security 的其他部分会解释这些权限并希望它们存在。使用基于用户名/密码的身份验证时, GrantedAuthority
实例通常由 UserDetailsService
加载。
You can obtain GrantedAuthority
instances from the <<`Authentication.getAuthorities(),servlet-authentication-authentication>> 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 UserDetailsService
.
通常,GrantedAuthority
对象是应用程序范围的权限。它们不特定于给定的域对象。因此,你不太可能有一个 GrantedAuthority
来表示对权限为 Employee
对象 54 的权限,因为如果存在数千个这样的权限,你将很快耗尽内存(或至少导致应用程序花很长时间来认证用户)。当然,Spring Security 的设计明确满足此常见要求,但你应该使用项目的域对象安全功能来解决此目的。
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.
AuthenticationManager
{security-api-url}org/springframework/security/authentication/AuthenticationManager.html[AuthenticationManager
]是定义 Spring Security 的过滤器 authentication执行方式的 API。返回的 <<`Authentication`,servlet-authentication-authentication>> 随后由触发了 AuthenticationManager`的控制器(即,Spring Security’s `Filters
instances)设置在 SecurityContextHolder上。如果你未与 Spring Security 的 Filters`实例集成,你可以直接设置 `SecurityContextHolder`且不需要使用 `AuthenticationManager
。
{security-api-url}org/springframework/security/authentication/AuthenticationManager.html[AuthenticationManager
] is the API that defines how Spring Security’s Filters perform authentication.
The <<`Authentication`,servlet-authentication-authentication>> that is returned is then set on the SecurityContextHolder by the controller (that is, by 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
.
虽然 AuthenticationManager
的实现可以是任何东西,但最常见的实现是 <<`ProviderManager`,servlet-authentication-providermanager>>。
While the implementation of AuthenticationManager
could be anything, the most common implementation is <<`ProviderManager`,servlet-authentication-providermanager>>.
ProviderManager
{security-api-url}org/springframework/security/authentication/ProviderManager.html[ProviderManager
] 是 <<`AuthenticationManager`,servlet-authentication-authenticationmanager>> 最常用的实现。ProviderManager`委托给 <<`AuthenticationProvider
,servlet-authentication-authenticationprovider>> 实例的 List
。每个 AuthenticationProvider`都有一个机会指出身份验证应成功、失败,或者指出它无法做出决定并允许下游 `AuthenticationProvider`决定。如果未配置的 `AuthenticationProvider`实例均无法进行身份验证,则身份验证会因出现 `ProviderNotFoundException`而失败,`ProviderNotFoundException`是一个特殊 `AuthenticationException
,指出未配置 `ProviderManager`以支持传入的 `Authentication`类型。
{security-api-url}org/springframework/security/authentication/ProviderManager.html[ProviderManager
] is the most commonly used implementation of <<`AuthenticationManager`,servlet-authentication-authenticationmanager>>.
ProviderManager
delegates to a List
of <<`AuthenticationProvider`,servlet-authentication-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.

在实践中,每个 AuthenticationProvider
知道如何执行特定类型的身份验证。例如,一个 AuthenticationProvider
可能会验证用户名/密码,而另一个可能会验证 SAML 断言。这能让每个 AuthenticationProvider
在支持多种身份验证类型且仅公开一个 AuthenticationManager
bean 的情况下,执行非常具体的身份验证类型。
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
还允许配置一个可选的父 AuthenticationManager
,在没有 AuthenticationProvider
可以执行身份验证的情况下咨询它。父元素可以是任何类型的 AuthenticationManager
,但它通常是 ProviderManager
的一个实例。
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
.

事实上,多个 ProviderManager
实例可以共享相同的父 AuthenticationManager
。这在有多个 @{6} 实例共享一些相同的身份验证(共享父 AuthenticationManager
),但身份验证机制又不相同的(不同的 ProviderManager
实例)的情况下比较常见。
In fact, multiple ProviderManager
instances might share the same parent AuthenticationManager
.
This is somewhat common in scenarios where there are multiple SecurityFilterChain
instances that have some authentication in common (the shared parent AuthenticationManager
), but also different authentication mechanisms (the different ProviderManager
instances).

默认情况下,ProviderManager
尝试清除成功身份验证请求返回的 Authentication
对象中的任何敏感凭据信息。这可以防止诸如密码之类的信息在 HttpSession
中保留的时间超过必要时间。
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
.
当使用用户对象的缓存时,可能会导致问题,例如,提高无状态应用程序的性能。如果 `Authentication`包含对缓存中对象的引用(例如 `UserDetails`实例)并且其凭据已删除,则无法再对缓存值进行身份验证。如果使用缓存,则需要考虑此问题。一个显而易见的解决方案是首先对对象进行复制,无论是在缓存实现还是在创建返回 `Authentication`对象的 `AuthenticationProvider`中。或者,可以在 `ProviderManager`上禁用 `eraseCredentialsAfterAuthentication`属性。请参阅 {security-api-url}org/springframework/security/authentication/ProviderManager.html[ProviderManager] 类的 Javadoc。
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[ProviderManager] class.
AuthenticationProvider
你可以注入多个 {security-api-url}org/springframework/security/authentication/AuthenticationProvider.html[AuthenticationProvider`s] instances into <<`ProviderManager
,servlet-authentication-providermanager>>。每个 AuthenticationProvider`会执行特定类型的认证。例如,`DaoAuthenticationProvider
支持基于用户名/密码的认证,而 `JwtAuthenticationProvider`支持验证 JWT 令牌。
You can inject multiple {security-api-url}org/springframework/security/authentication/AuthenticationProvider.html[AuthenticationProvider`s] instances into <<`ProviderManager
,servlet-authentication-providermanager>>.
Each AuthenticationProvider
performs a specific type of authentication.
For example, DaoAuthenticationProvider
supports username/password-based authentication, while JwtAuthenticationProvider
supports authenticating a JWT token.
Request Credentials with AuthenticationEntryPoint
{security-api-url}org/springframework/security/web/AuthenticationEntryPoint.html[AuthenticationEntryPoint
] 用于发送一个 HTTP 响应,该响应请求客户端提供凭据。
{security-api-url}org/springframework/security/web/AuthenticationEntryPoint.html[AuthenticationEntryPoint
] is used to send an HTTP response that requests credentials from a client.
有时候,客户端主动包含凭证(例如用户名和密码)来请求资源。在这些情况下,Spring Security 不需要提供要求客户端提供凭据的 HTTP 响应,因为它们已经包含在内了。
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.
在其他情况下,客户端会向其无权访问的资源发出未经验证的请求。在这种情况下,会使用 `AuthenticationEntryPoint`的实现向客户端请求凭证。该 `AuthenticationEntryPoint`实现可能执行 redirect to a log in page,使用 WWW-Authenticate标题做出响应或采取其他操作。
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 redirect to a log in page, respond with an WWW-Authenticate header, or take other action.
AbstractAuthenticationProcessingFilter
{security-api-url}org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.html[AbstractAuthenticationProcessingFilter
] 用作用户凭据的身份验证的基 Filter
。在对凭据进行身份验证之前,Spring 安全通常使用 <<`AuthenticationEntryPoint`,servlet-authentication-authenticationentrypoint>> 请求凭据。
{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 <<`AuthenticationEntryPoint`,servlet-authentication-authenticationentrypoint>>.
接下来,AbstractAuthenticationProcessingFilter
可以对提交给它的任何身份验证请求进行身份验证。
Next, the AbstractAuthenticationProcessingFilter
can authenticate any authentication requests that are submitted to it.

当用户提交其凭证时,
AbstractAuthenticationProcessingFilter`会根据 `HttpServletRequest`创建一个待认证的 <<`Authentication
,servlet-authentication-authentication>>。创建的 Authentication`类型取决于 `AbstractAuthenticationProcessingFilter`的子类。例如,`UsernamePasswordAuthenticationFilter
根据在 HttpServletRequest`中提交的 _username_和 _password_创建一个 `UsernamePasswordAuthenticationToken
。
When the user submits their credentials, the
AbstractAuthenticationProcessingFilter
creates an <<`Authentication`,servlet-authentication-authentication>> from the HttpServletRequest
to be authenticated.
The type of Authentication
created depends on the subclass of AbstractAuthenticationProcessingFilter
.
For example, UsernamePasswordAuthenticationFilter
creates a UsernamePasswordAuthenticationToken
from a username and password that are submitted in the HttpServletRequest
.
接下来,将 <<`Authentication`,servlet-authentication-authentication>> 传递到 <<`AuthenticationManager`,servlet-authentication-authenticationmanager>> 进行身份验证。
Next, the <<`Authentication`,servlet-authentication-authentication>> is passed into the <<`AuthenticationManager`,servlet-authentication-authenticationmanager>> to be authenticated.
如果身份验证失败,则_Failure_。
If authentication fails, then Failure.
-
The 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.
如果身份验证成功,则 Success
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 Authentication is set on the SecurityContextHolder. Later, if you need to save the
SecurityContext
so that it can be automatically set on future requests,SecurityContextRepository#saveContext
must be explicitly invoked. See the {security-api-url}org/springframework/security/web/context/SecurityContextHolderFilter.html[SecurityContextHolderFilter
] 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 anInteractiveAuthenticationSuccessEvent
. -
AuthenticationSuccessHandler
is invoked. See the {security-api-url}org/springframework/security/web/authentication/AuthenticationSuccessHandler.html[AuthenticationSuccessHandler
] interface.