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

Servlet 身份验证体系结构

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

SecurityContextHolder

Spring Security 身份验证模型的核心是 . 它包含 SecurityContextSecurityContextHolderspring-doc.cn

SecurityContextHolder

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

指示用户已通过身份验证的最简单方法是直接设置:SecurityContextHolderspring-doc.cn

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

要获取有关经过身份验证的委托人的信息,请访问 .SecurityContextHolderspring-doc.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

默认情况下,使用 a 来存储这些详细信息,这意味着 for any for methods in same thread, 即使 that 没有作为参数显式传递给这些方法。 如果你在处理当前主体的请求后小心地清除线程,以这种方式使用 a 是非常安全的。 Spring Security 的FilterChainProxy确保始终清除。SecurityContextHolderThreadLocalSecurityContextSecurityContextThreadLocalSecurityContextspring-doc.cn

某些应用程序并不完全适合使用 ,因为它们使用线程的方式特定。 例如,Swing 客户端可能希望 Java 虚拟机中的所有线程都使用相同的安全上下文。 您可以在启动时使用策略进行配置,以指定您希望如何存储上下文。 对于独立应用程序,您将使用该策略。 其他应用程序可能希望安全线程生成的线程也采用相同的安全身份。 您可以通过使用 来实现此目的。 您可以通过两种方式更改默认模式。 第一种是设置系统属性。 第二种是在 上调用 static 方法 。 大多数应用程序不需要更改默认值。 但是,如果您这样做,请查看 JavaDoc 以了解更多信息。ThreadLocalSecurityContextHolderSecurityContextHolder.MODE_GLOBALSecurityContextHolder.MODE_INHERITABLETHREADLOCALSecurityContextHolder.MODE_THREADLOCALSecurityContextHolderSecurityContextHolderspring-doc.cn

SecurityContext

SecurityContext是从SecurityContextHolder获取的。 它包含一个 Authentication 对象。SecurityContextspring-doc.cn

认证

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

包含:Authenticationspring-doc.cn

  • principal:标识用户。 使用用户名/密码进行身份验证时,这通常是 UserDetails 的实例。spring-doc.cn

  • credentials:通常是密码。 在许多情况下,在用户通过身份验证后会清除此漏洞,以确保其不会泄露。spring-doc.cn

  • authoritiesGrantedAuthority 实例是授予用户的高级权限。 两个示例是 roles 和 scopes。spring-doc.cn

授予权限

GrantedAuthority 实例是授予用户的高级权限。 两个示例是 roles 和 scopes。spring-doc.cn

您可以从 Authentication.getAuthorities() 方法获取实例。 此方法提供 of 对象。 毫不奇怪,A 是授予委托人的授权。 此类颁发机构通常是“角色”,例如 或 。 这些角色稍后将配置为 Web 授权、方法授权和域对象授权。 Spring Security 的其他部分解释这些权威并期望它们存在。 使用基于用户名/密码的身份验证实例通常由 UserDetailsService 加载。GrantedAuthorityCollectionGrantedAuthorityGrantedAuthorityROLE_ADMINISTRATORROLE_HR_SUPERVISORGrantedAuthorityspring-doc.cn

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

身份验证管理器

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

虽然 的实现可以是任何东西,但最常见的实现是 ProviderManagerAuthenticationManagerspring-doc.cn

提供者管理器

ProviderManagerAuthenticationManager 最常用的实现。 delegates 传递给 AuthenticationProvider 实例。 每个都有机会指示身份验证应该成功、失败或指示它无法做出决定并允许下游做出决定。 如果所有配置的实例都无法进行身份验证,则身份验证将失败,并显示 ,这是一个特殊情况,表示 未配置为支持传入其中的类型。ProviderManagerListAuthenticationProviderAuthenticationProviderAuthenticationProviderProviderNotFoundExceptionAuthenticationExceptionProviderManagerAuthenticationspring-doc.cn

providermanager

在实践中,每个 Wizard 都知道如何执行特定类型的身份验证。 例如,一个用户可能能够验证用户名/密码,而另一个用户可能能够验证 SAML 断言。 这允许每个 bean 执行非常特定类型的身份验证,同时支持多种类型的身份验证,并且只公开单个 bean。AuthenticationProviderAuthenticationProviderAuthenticationProviderAuthenticationManagerspring-doc.cn

ProviderManager还允许配置可选的 parent ,如果 No Can 执行身份验证,则会查询该 parent。 父级可以是 的任何类型的 ,但它通常是 的实例。AuthenticationManagerAuthenticationProviderAuthenticationManagerProviderManagerspring-doc.cn

ProviderManager 父级

实际上,多个实例可能共享同一 parent 。 这在有多个 SecurityFilterChain 实例具有一些共同的身份验证(共享父级)但具有不同的身份验证机制(不同的实例)的情况下有些常见。ProviderManagerAuthenticationManagerAuthenticationManagerProviderManagerspring-doc.cn

ProviderManagers 父级

默认情况下,尝试从成功的身份验证请求返回的对象中清除任何敏感凭证信息。 这样可以防止信息(如密码)在 .ProviderManagerAuthenticationHttpSessionspring-doc.cn

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

AuthenticationProvider 认证

可以将多个 AuthenticationProvider实例注入 ProviderManager。 每个 VPN 都执行特定类型的身份验证。 例如,DaoAuthenticationProvider 支持基于用户名/密码的身份验证,同时支持对 JWT 令牌进行身份验证。AuthenticationProviderJwtAuthenticationProviderspring-doc.cn

请求凭证AuthenticationEntryPoint

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

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

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

AbstractAuthenticationProcessingFilter

AbstractAuthenticationProcessingFilter用作验证用户凭据的基础。 在对凭据进行身份验证之前, Spring Security 通常使用AuthenticationEntryPoint请求凭据。Filterspring-doc.cn

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

abstractauthenticationprocessingfilter

数字 1当用户提交其凭证时,将从 中创建一个 身份验证 。 创建的类型取决于 的子类。 例如,UsernamePasswordAuthenticationFilter 会创建一个 from 在 . AbstractAuthenticationProcessingFilterHttpServletRequestAuthenticationAbstractAuthenticationProcessingFilterUsernamePasswordAuthenticationTokenHttpServletRequestspring-doc.cn

编号 2接下来,将 Authentication 传递到 AuthenticationManager 中进行身份验证。spring-doc.cn

编号 3如果身份验证失败,则为 Failurespring-doc.cn

编号 4如果身份验证成功,则为 Successspring-doc.cn