对于最新的稳定版本,请使用 Spring Security 6.5.3spring-doc.cadn.net.cn

Servlet 身份验证体系结构

本讨论扩展了 Servlet Security: The Big Picture,以描述 Servlet 身份验证中使用的 Spring Security 的主要架构组件。 如果您需要具体的流程来解释这些部分如何组合在一起,请查看身份验证机制的特定部分。spring-doc.cadn.net.cn

安全上下文持有者

Spring Security 身份验证模型的核心是SecurityContextHolder. 它包含 SecurityContextspring-doc.cadn.net.cn

securitycontextholder

SecurityContextHolder是 Spring Security 存储谁经过身份验证的详细信息的地方。 Spring Security 不关心SecurityContextHolder已填充。 如果它包含一个值,则将其用作当前经过身份验证的用户。spring-doc.cadn.net.cn

指示用户已通过身份验证的最简单方法是将SecurityContextHolder径直:spring-doc.cadn.net.cn

示例 1.设置SecurityContextHolder
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 我们首先创建一个空的SecurityContext. 您应该创建一个新的SecurityContext实例而不是使用SecurityContextHolder.getContext().setAuthentication(authentication)以避免跨多个线程的争用条件。
2 接下来,我们创建一个新的Authentication对象。 Spring Security 不在乎什么类型的Authentication实现设置在SecurityContext. 在这里,我们使用TestingAuthenticationToken,因为它非常简单。 更常见的生产场景是UsernamePasswordAuthenticationToken(userDetails, password, authorities).
3 最后,我们将SecurityContextSecurityContextHolder. Spring Security 使用此信息进行授权

要获取有关经过身份验证的主体的信息,请访问SecurityContextHolder.spring-doc.cadn.net.cn

访问当前经过身份验证的用户
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总是被清除。spring-doc.cadn.net.cn

某些应用程序并不完全适合使用ThreadLocal,因为它们使用线程的特定方式。 例如,Swing 客户端可能希望 Java 虚拟机中的所有线程都使用相同的安全上下文。 您可以配置SecurityContextHolder在启动时使用策略来指定您希望如何存储上下文。 对于独立应用程序,您可以使用SecurityContextHolder.MODE_GLOBAL策略。 其他应用程序可能希望安全线程生成的线程也采用相同的安全标识。 您可以通过使用SecurityContextHolder.MODE_INHERITABLETHREADLOCAL. 您可以将模式从默认值更改为SecurityContextHolder.MODE_THREADLOCAL有两个方面。 首先是设置系统属性。 第二种是在SecurityContextHolder. 大多数应用程序不需要从默认值更改。 但是,如果您这样做,请查看 JavaDocSecurityContextHolder了解更多。spring-doc.cadn.net.cn

安全上下文

SecurityContextSecurityContextHolder 获取。 这SecurityContext包含一个 Authentication 对象。spring-doc.cadn.net.cn

认证

Authentication接口在 Spring Security 中有两个主要用途:spring-doc.cadn.net.cn

Authentication包含:spring-doc.cadn.net.cn

授予权限

GrantedAuthority实例是授予用户的高级权限。两个示例是角色和范围。spring-doc.cadn.net.cn

您可以获得GrantedAuthority实例Authentication.getAuthorities()方法。 此方法提供了一个CollectionGrantedAuthority对象。 一个GrantedAuthority毫不奇怪,是授予委托人的权限。此类权限通常是“角色”,例如ROLE_ADMINISTRATORROLE_HR_SUPERVISOR. 稍后将为Web授权、方法授权和域对象授权配置这些角色。 Spring Security 的其他部分解释这些权限并期望它们存在。 使用基于用户名/密码的身份验证时GrantedAuthority实例通常由UserDetailsService.spring-doc.cadn.net.cn

通常,GrantedAuthority对象是应用程序范围的权限。 它们不特定于给定的域对象。 因此,您不太可能拥有GrantedAuthority表示对Employee对象编号 54,因为如果有数千个这样的权限,您将很快耗尽内存(或者至少会导致应用程序需要很长时间来验证用户)。 当然,Spring Security 是专门为处理这一常见需求而设计的,但您应该为此目的使用项目的域对象安全功能。spring-doc.cadn.net.cn

身份验证管理器

AuthenticationManager是定义 Spring Security 的过滤器如何执行身份验证的 API。 这Authentication然后由控制器在 SecurityContextHolder 上设置返回的 (即,通过Spring Security 的Filters实例)调用AuthenticationManager. 如果您不与 Spring Security 的Filters实例中,您可以将SecurityContextHolder直接使用,并且不需要使用AuthenticationManager.spring-doc.cadn.net.cn

AuthenticationManager可以是任何东西,最常见的实现是ProviderManager.spring-doc.cadn.net.cn

ProviderManager (提供者管理器)

ProviderManager是最常用的实现AuthenticationManager.ProviderManager委托给ListAuthenticationProvider实例。 每AuthenticationProvider有机会指示身份验证应该成功、失败或指示它无法做出决定并允许下游AuthenticationProvider来决定。 如果没有配置的AuthenticationProvider实例可以进行身份验证,身份验证失败,并显示ProviderNotFoundException,这是一个特殊的AuthenticationException这表示ProviderManager未配置为支持Authentication它被传递到它里面。spring-doc.cadn.net.cn

providermanager

在实践中,每个AuthenticationProvider知道如何执行特定类型的身份验证。 例如,一个AuthenticationProvider可能能够验证用户名/密码,而另一个可能能够对 SAML 断言进行身份验证。 这让每个AuthenticationProvider执行非常特定类型的身份验证,同时支持多种类型的身份验证,并且仅公开单个身份验证AuthenticationManager豆。spring-doc.cadn.net.cn

ProviderManager还允许配置可选的父级AuthenticationManager,如果没有AuthenticationProvider可以执行身份验证。 父级可以是任何类型的AuthenticationManager,但它通常是ProviderManager.spring-doc.cadn.net.cn

providermanager 父级

事实上,多个ProviderManager实例可能共享同一父级AuthenticationManager. 这在有多个SecurityFilterChain具有一些共同身份验证的实例(共享父级AuthenticationManager),但也有不同的身份验证机制(不同的ProviderManager实例)。spring-doc.cadn.net.cn

providermanagers 父级

默认情况下,ProviderManager尝试从Authentication成功身份验证请求返回的对象。这可以防止信息(例如密码)在HttpSession.spring-doc.cadn.net.cn

当您使用用户对象的缓存时,这可能会导致问题,例如,为了提高无状态应用程序的性能。如果Authentication包含对缓存中对象的引用(例如UserDetails实例),并且其凭据已被删除,则无法再针对缓存值进行身份验证。如果您使用缓存,则需要考虑这一点。一个明显的解决方案是首先在缓存实现中或在AuthenticationProvider创建返回的Authentication对象。 或者,您可以禁用eraseCredentialsAfterAuthentication属性ProviderManager. 有关 Javadoc 类,请参阅 Javadocspring-doc.cadn.net.cn

身份验证提供程序

您可以注入多个AuthenticationProviders实例转换为ProviderManager. 每AuthenticationProvider执行特定类型的身份验证。 例如DaoAuthenticationProvider支持基于用户名/密码的身份验证,而JwtAuthenticationProvider支持对 JWT Tokens进行身份验证。spring-doc.cadn.net.cn

请求凭据AuthenticationEntryPoint

AuthenticationEntryPoint用于发送从客户端请求凭据的 HTTP 响应。spring-doc.cadn.net.cn

有时,客户端会主动包含凭据(例如用户名和密码)来请求资源。 在这些情况下,Spring Security 不需要提供从客户端请求凭据的 HTTP 响应,因为它们已经包含在内。spring-doc.cadn.net.cn

在其他情况下,客户端会向他们无权访问的资源发出未经身份验证的请求。在这种情况下,实现AuthenticationEntryPoint用于向客户端请求凭据。 这AuthenticationEntryPoint实现可能会执行重定向到登录页面,使用 WWW-Authenticate 标头进行响应,或采取其他作。spring-doc.cadn.net.cn

抽象身份验证处理过滤器

AbstractAuthenticationProcessingFilter用作基础Filter用于验证用户的凭据。在对凭据进行身份验证之前,Spring Security 通常使用AuthenticationEntryPoint.spring-doc.cadn.net.cn

接下来,AbstractAuthenticationProcessingFilter可以对提交给它的任何身份验证请求进行身份验证。spring-doc.cadn.net.cn

抽象身份验证处理过滤器

1号当用户提交其凭据时,AbstractAuthenticationProcessingFilter创建一个AuthenticationHttpServletRequest待认证。 的类型Authenticationcreated 取决于AbstractAuthenticationProcessingFilter. 例如UsernamePasswordAuthenticationFilter创建一个UsernamePasswordAuthenticationToken从在 HttpServletRequest.spring-doc.cadn.net.cn

2号接下来,Authentication被传递到AuthenticationManager待认证。spring-doc.cadn.net.cn

3号如果身份验证失败,则失败spring-doc.cadn.net.cn

4号如果身份验证成功,则成功spring-doc.cadn.net.cn