本讨论扩展了 Servlet 安全性:大局 来描述 Servlet 身份验证中使用的 Spring Security 的主要体系结构组件。 如果您需要具体的流程来解释这些部分如何组合在一起,请查看 身份验证机制 特定部分。
-
SecurityContextHolder - 这是 Spring Security 存储经过身份验证者的详细信息的地方。
SecurityContextHolder
-
SecurityContext - 从 获取并包含当前经过身份验证的用户。
SecurityContextHolder
Authentication
-
身份验证 - 可以是输入,用于提供用户提供的用于身份验证的凭据或当前用户。
AuthenticationManager
SecurityContext
-
GrantedAuthority - 授予委托人(即角色、范围等)的授权
Authentication
-
AuthenticationManager - 定义 Spring Security 的过滤器如何执行身份验证的 API。
-
ProviderManager - 最常见的 .
AuthenticationManager
-
AuthenticationProvider - 用于执行特定类型的身份验证。
ProviderManager
-
使用
AuthenticationEntryPoint
请求凭据 - 用于从客户端请求凭据(即重定向到登录页面、发送响应等)WWW-Authenticate
-
AbstractAuthenticationProcessingFilter - 用于身份验证的基础。 这也很好地说明了高级身份验证流程以及各个部分如何协同工作。
Filter
SecurityContextHolder
Spring Security 身份验证模型的核心是 .
它包含 SecurityContext。SecurityContextHolder
这是 Spring Security 存储谁经过身份验证的详细信息的地方。
Spring Security 不关心 是如何填充的。
如果它包含值,则将其用作当前经过身份验证的用户。SecurityContextHolder
SecurityContextHolder
指示用户已通过身份验证的最简单方法是直接设置:SecurityContextHolder
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 | 我们首先创建一个空的 .
您应该创建一个新实例而不是 using 以避免跨多个线程的争用条件。SecurityContext SecurityContext SecurityContextHolder.getContext().setAuthentication(authentication) |
2 | 接下来,我们创建一个新的 Authentication 对象。
Spring Security 并不关心在 .
这里我们使用 ,因为它非常简单。
更常见的生产场景是 。Authentication SecurityContext TestingAuthenticationToken UsernamePasswordAuthenticationToken(userDetails, password, authorities) |
3 | 最后,我们将 .
Spring Security 使用此信息进行授权。SecurityContext SecurityContextHolder |
要获取有关经过身份验证的委托人的信息,请访问 .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
默认情况下,使用 a 来存储这些详细信息,这意味着 for any for methods in same thread, 即使 that 没有作为参数显式传递给这些方法。
如果你在处理当前主体的请求后小心地清除线程,以这种方式使用 a 是非常安全的。
Spring Security 的FilterChainProxy确保始终清除。SecurityContextHolder
ThreadLocal
SecurityContext
SecurityContext
ThreadLocal
SecurityContext
某些应用程序并不完全适合使用 ,因为它们使用线程的方式特定。
例如,Swing 客户端可能希望 Java 虚拟机中的所有线程都使用相同的安全上下文。
您可以在启动时使用策略进行配置,以指定您希望如何存储上下文。
对于独立应用程序,您将使用该策略。
其他应用程序可能希望安全线程生成的线程也采用相同的安全身份。
您可以通过使用 来实现此目的。
您可以通过两种方式更改默认模式。
第一种是设置系统属性。
第二种是在 上调用 static 方法 。
大多数应用程序不需要更改默认值。
但是,如果您这样做,请查看 JavaDoc 以了解更多信息。ThreadLocal
SecurityContextHolder
SecurityContextHolder.MODE_GLOBAL
SecurityContextHolder.MODE_INHERITABLETHREADLOCAL
SecurityContextHolder.MODE_THREADLOCAL
SecurityContextHolder
SecurityContextHolder
1 | 我们首先创建一个空的 .
您应该创建一个新实例而不是 using 以避免跨多个线程的争用条件。SecurityContext SecurityContext SecurityContextHolder.getContext().setAuthentication(authentication) |
2 | 接下来,我们创建一个新的 Authentication 对象。
Spring Security 并不关心在 .
这里我们使用 ,因为它非常简单。
更常见的生产场景是 。Authentication SecurityContext TestingAuthenticationToken UsernamePasswordAuthenticationToken(userDetails, password, authorities) |
3 | 最后,我们将 .
Spring Security 使用此信息进行授权。SecurityContext SecurityContextHolder |
SecurityContext
SecurityContext
是从SecurityContextHolder获取的。
它包含一个 Authentication 对象。SecurityContext
认证
Authentication
接口在 Spring Security 中有两个主要用途:
-
AuthenticationManager
的输入,用于提供用户提供的用于身份验证的凭证。 在此方案中使用时,返回 。isAuthenticated()
false
-
表示当前经过身份验证的用户。 您可以从 SecurityContext 获取 current。
Authentication
包含:Authentication
-
principal
:标识用户。 使用用户名/密码进行身份验证时,这通常是UserDetails
的实例。 -
credentials
:通常是密码。 在许多情况下,在用户通过身份验证后会清除此漏洞,以确保其不会泄露。 -
authorities
:GrantedAuthority
实例是授予用户的高级权限。 两个示例是 roles 和 scopes。
授予权限
GrantedAuthority
实例是授予用户的高级权限。
两个示例是 roles 和 scopes。
您可以从 Authentication.getAuthorities()
方法获取实例。
此方法提供 of 对象。
毫不奇怪,A 是授予委托人的授权。
此类颁发机构通常是“角色”,例如 或 。
这些角色稍后将配置为 Web 授权、方法授权和域对象授权。
Spring Security 的其他部分解释这些权威并期望它们存在。
使用基于用户名/密码的身份验证实例通常由 UserDetailsService
加载。GrantedAuthority
Collection
GrantedAuthority
GrantedAuthority
ROLE_ADMINISTRATOR
ROLE_HR_SUPERVISOR
GrantedAuthority
通常,对象是应用程序范围的权限。
它们并不特定于给定的域对象。
因此,您不太可能拥有表示对象编号 54 的权限,因为如果有数千个这样的权限,您很快就会耗尽内存(或者,至少会导致应用程序花费很长时间来验证用户)。
当然, Spring Security 是专门为处理这个常见需求而设计的,但是您应该改用项目的域对象安全功能来实现此目的。GrantedAuthority
GrantedAuthority
Employee
身份验证管理器
AuthenticationManager
是定义 Spring Security 的过滤器如何执行身份验证的 API。
然后,由调用的控制器(即 Spring Security 的 Filters
实例)在 SecurityContextHolder 上设置返回的 Authentication
。
如果您未与 Spring Security 的实例集成,则可以直接设置 ,并且不需要使用 .AuthenticationManager
Filters
SecurityContextHolder
AuthenticationManager
虽然 的实现可以是任何东西,但最常见的实现是 ProviderManager
。AuthenticationManager
提供者管理器
ProviderManager
是 AuthenticationManager
最常用的实现。 delegates 传递给 AuthenticationProvider
实例。
每个都有机会指示身份验证应该成功、失败或指示它无法做出决定并允许下游做出决定。
如果所有配置的实例都无法进行身份验证,则身份验证将失败,并显示 ,这是一个特殊情况,表示 未配置为支持传入其中的类型。ProviderManager
List
AuthenticationProvider
AuthenticationProvider
AuthenticationProvider
ProviderNotFoundException
AuthenticationException
ProviderManager
Authentication
在实践中,每个 Wizard 都知道如何执行特定类型的身份验证。
例如,一个用户可能能够验证用户名/密码,而另一个用户可能能够验证 SAML 断言。
这允许每个 bean 执行非常特定类型的身份验证,同时支持多种类型的身份验证,并且只公开单个 bean。AuthenticationProvider
AuthenticationProvider
AuthenticationProvider
AuthenticationManager
ProviderManager
还允许配置可选的 parent ,如果 No Can 执行身份验证,则会查询该 parent。
父级可以是 的任何类型的 ,但它通常是 的实例。AuthenticationManager
AuthenticationProvider
AuthenticationManager
ProviderManager
实际上,多个实例可能共享同一 parent 。
这在有多个 SecurityFilterChain
实例具有一些共同的身份验证(共享父级)但具有不同的身份验证机制(不同的实例)的情况下有些常见。ProviderManager
AuthenticationManager
AuthenticationManager
ProviderManager
默认情况下,尝试从成功的身份验证请求返回的对象中清除任何敏感凭证信息。
这样可以防止信息(如密码)在 .ProviderManager
Authentication
HttpSession
当您使用用户对象的缓存时,这可能会导致问题,例如,为了提高无状态应用程序的性能。
如果 包含对缓存中对象(如实例)的引用,并且已删除其凭据,则无法再对缓存的值进行身份验证。
如果您使用缓存,则需要考虑到这一点。
一个明显的解决方案是首先在 cache 实现或创建返回对象的 中创建对象的副本。
或者,您也可以禁用 上的属性。
请参阅 ProviderManager 类的 Javadoc。Authentication
UserDetails
AuthenticationProvider
Authentication
eraseCredentialsAfterAuthentication
ProviderManager
AuthenticationProvider 认证
可以将多个 AuthenticationProvider
实例注入 ProviderManager
。
每个 VPN 都执行特定类型的身份验证。
例如,DaoAuthenticationProvider
支持基于用户名/密码的身份验证,同时支持对 JWT 令牌进行身份验证。AuthenticationProvider
JwtAuthenticationProvider
请求凭证AuthenticationEntryPoint
AuthenticationEntryPoint
用于发送从客户端请求凭据的 HTTP 响应。
有时,客户端会主动包含凭证(例如用户名和密码)来请求资源。 在这些情况下, Spring Security 不需要提供从 Client 端请求凭据的 HTTP 响应,因为它们已经包含在内。
在其他情况下,客户端会向他们无权访问的资源发出未经身份验证的请求。
在这种情况下,使用 的实现从客户端请求凭证。
该实现可能会执行重定向到登录页面、使用 WWW-Authenticate 标头进行响应或执行其他操作。AuthenticationEntryPoint
AuthenticationEntryPoint
AbstractAuthenticationProcessingFilter
AbstractAuthenticationProcessingFilter
用作验证用户凭据的基础。
在对凭据进行身份验证之前, Spring Security 通常使用AuthenticationEntryPoint
请求凭据。Filter
接下来,可以对提交给它的任何身份验证请求进行身份验证。AbstractAuthenticationProcessingFilter
当用户提交其凭证时,将从 中创建一个 身份验证
。
创建的类型取决于 的子类。
例如,UsernamePasswordAuthenticationFilter
会创建一个 from 在 . AbstractAuthenticationProcessingFilter
HttpServletRequest
Authentication
AbstractAuthenticationProcessingFilter
UsernamePasswordAuthenticationToken
HttpServletRequest
接下来,将 Authentication
传递到 AuthenticationManager
中进行身份验证。
如果身份验证失败,则为 Failure。
-
RememberMeServices.loginFail
被调用。 如果未配置 Remember me,则为 no-op。 请参阅rememberme
软件包。 -
AuthenticationFailureHandler
被调用。 参见AuthenticationFailureHandler
接口。
如果身份验证成功,则为 Success。
-
SessionAuthenticationStrategy
收到新登录的通知。 参见SessionAuthenticationStrategy
接口。 -
Authentication在SecurityContextHolder上设置。 稍后,如果需要保存 ,以便在将来的请求中自动设置它,则必须显式调用。 参见
SecurityContextHolderFilter
类。SecurityContext
SecurityContextRepository#saveContext
-
RememberMeServices.loginSuccess
被调用。 如果未配置 Remember me,则为 no-op。 请参阅rememberme
软件包。 -
ApplicationEventPublisher
发布一个 .InteractiveAuthenticationSuccessEvent
-
AuthenticationSuccessHandler
被调用。 参见AuthenticationSuccessHandler
接口。