对于最新的稳定版本,请使用 Spring Security 6.3.1Spring中文文档

对于最新的稳定版本,请使用 Spring Security 6.3.1Spring中文文档

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

SecurityContextHolder

Spring Security 身份验证模型的核心是 . 它包含 SecurityContextSecurityContextHolderSpring中文文档

securitycontextholder

这是 Spring Security 存储身份验证人员的详细信息的地方。 Spring Security 不关心如何填充。 如果它包含一个值,则它被用作当前经过身份验证的用户。SecurityContextHolderSecurityContextHolderSpring中文文档

指示用户已通过身份验证的最简单方法是直接设置。SecurityContextHolderSpring中文文档

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

如果要获取有关经过身份验证的主体的信息,可以通过访问 .SecurityContextHolderSpring中文文档

访问当前经过身份验证的用户
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 来存储这些详细信息,这意味着 始终可用于同一线程中的方法来,即使 没有显式地作为参数传递给这些方法。 如果在处理当前主体的请求后注意清除线程,则以这种方式使用 a 是非常安全的。 Spring Security 的 FilterChainProxy 确保始终清除。SecurityContextHolderThreadLocalSecurityContextSecurityContextThreadLocalSecurityContextSpring中文文档

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

1 我们首先创建一个空的 . 重要的是创建一个新实例,而不是使用以避免跨多个线程的争用条件。SecurityContextSecurityContextSecurityContextHolder.getContext().setAuthentication(authentication)
2 接下来,我们创建一个新的 Authentication 对象。 Spring Security 不关心在 . 在这里我们使用,因为它非常简单。 更常见的生产方案是 。AuthenticationSecurityContextTestingAuthenticationTokenUsernamePasswordAuthenticationToken(userDetails, password, authorities)
3 最后,我们将 on 设置为 . Spring Security 将使用此信息进行授权SecurityContextSecurityContextHolder

安全上下文

SecurityContext 是从 SecurityContextHolder 获取的。 包含一个 Authentication 对象。SecurityContextSpring中文文档

认证

身份验证在 Spring Security 中有两个主要目的:Spring中文文档

包含:AuthenticationSpring中文文档

授予权限

GrantedAuthority 是授予用户的高级权限。一些示例是角色或作用域。Spring中文文档

GrantedAuthority可以从 Authentication.getAuthorities() 方法获取。 此方法提供 of 对象。 毫不奇怪,A 是授予委托人的权力。 这种权限通常是“角色”,例如 或 。 稍后将针对 Web 授权、方法授权和域对象授权配置这些角色。 Spring Security 的其他部分能够解释这些权限,并期望它们存在。 使用基于用户名/密码的身份验证时,通常由 UserDetailsService 加载。CollectionGrantedAuthorityGrantedAuthorityROLE_ADMINISTRATORROLE_HR_SUPERVISORGrantedAuthoritySpring中文文档

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

AuthenticationManager(身份验证管理器)

AuthenticationManager 是定义 Spring Security 过滤器如何执行身份验证的 API。 然后,返回身份验证由调用 . 如果您没有与 Spring Security 的过滤器集成,您可以直接设置 并且不需要使用 .AuthenticationManagerSecurityContextHolderAuthenticationManagerSpring中文文档

虽然实现可以是任何东西,但最常见的实现是 ProviderManagerAuthenticationManagerSpring中文文档

ProviderManager

ProviderManagerAuthenticationManager 最常用的实现。 委托给 AuthenticationProvider s。 每个都有机会指示身份验证应该成功、失败,或者指示它无法做出决定并允许下游做出决定。 如果配置的 s 都无法进行身份验证,则身份验证将失败,这是一个特殊值,表示未配置为支持传递到其中的类型。ProviderManagerListAuthenticationProviderAuthenticationProviderAuthenticationProviderProviderNotFoundExceptionAuthenticationExceptionProviderManagerAuthenticationSpring中文文档

providermanager

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

ProviderManager还允许配置可选的父级,在无法执行身份验证的情况下会咨询该父级。 父级可以是 的任何类型,但它通常是 的实例。AuthenticationManagerAuthenticationProviderAuthenticationManagerProviderManagerSpring中文文档

providerManager 父级

事实上,多个实例可能共享同一个父级。 这在有多个 SecurityFilterChain 实例的情况下很常见,这些实例具有一些共同的身份验证(共享父级),但也具有不同的身份验证机制(不同的实例)。ProviderManagerAuthenticationManagerAuthenticationManagerProviderManagerSpring中文文档

providermanagers 父级

默认情况下,将尝试从成功身份验证请求返回的对象中清除任何敏感凭据信息。 这样可以防止密码等信息在 .ProviderManagerAuthenticationHttpSessionSpring中文文档

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

AuthenticationProvider

可以将多个 AuthenticationProvider 注入到 ProviderManager 中。 每个都执行特定类型的身份验证。 例如,DaoAuthenticationProvider 支持基于用户名/密码的身份验证,同时支持对 JWT 令牌进行身份验证。AuthenticationProviderJwtAuthenticationProviderSpring中文文档

请求凭据AuthenticationEntryPoint

AuthenticationEntryPoint 用于发送从客户端请求凭据的 HTTP 响应。Spring中文文档

有时,客户端会主动包含用户名/密码等凭据来请求资源。 在这些情况下,Spring Security 不需要提供从客户端请求凭据的 HTTP 响应,因为它们已经包含在内。Spring中文文档

在其他情况下,客户端将向他们无权访问的资源发出未经身份验证的请求。 在本例中,使用 的实现从客户端请求凭据。 实现可能会执行重定向到登录页面,使用 WWW-Authenticate 标头进行响应等。AuthenticationEntryPointAuthenticationEntryPointSpring中文文档

AbstractAuthenticationProcessingFilter

AbstractAuthenticationProcessingFilter 用作对用户凭据进行身份验证的基础。 在对凭据进行身份验证之前,Spring Security 通常使用 AuthenticationEntryPoint 请求凭据。FilterSpring中文文档

接下来,可以对提交给它的任何身份验证请求进行身份验证。AbstractAuthenticationProcessingFilterSpring中文文档

抽象身份验证处理过滤器

数字 1当用户提交其凭据时,将从要进行身份验证的 Authentication 创建身份验证。 创建的类型取决于 的子类。 例如,UsernamePasswordAuthenticationFilter 从在 . AbstractAuthenticationProcessingFilterHttpServletRequestAuthenticationAbstractAuthenticationProcessingFilterUsernamePasswordAuthenticationTokenHttpServletRequestSpring中文文档

数字 2接下来,将身份验证传递到 AuthenticationManager 中进行身份验证。Spring中文文档

数字 3如果身份验证失败,则失败Spring中文文档

数字 4如果身份验证成功,则为成功Spring中文文档