红茶的个人站点

  • 首页
  • 专栏
  • 开发工具
  • 其它
  • 隐私政策
Awalon
Talk is cheap,show me the code.
  1. 首页
  2. Spring Framework 学习笔记
  3. 正文

Spring Security 学习笔记 3:认证模型

2026年1月4日 13点热度 0人点赞 0条评论

Spring Security 认证模型的核心是 SecurityContextHolder 。它包含 SecurityContext。

securitycontextholder

SecurityContextHolder

SecurityContextHolder 是 Spring Security 存储已认证用户信息的地方。Spring Security 不关心 SecurityContextHolder 是如何被填充的。如果其中包含值,该值将被用作当前已认证的用户。

表示用户已认证的最简单方式是直接设置 SecurityContextHolder :

SecurityContext emptyContext = SecurityContextHolder.createEmptyContext();
Authentication authentication = new TestingAuthenticationToken(
        "username", "password", "ROLE_USER");
emptyContext.setAuthentication(authentication);
SecurityContextHolder.setContext(emptyContext);

这里通过SecurityContextHolder.createEmptyContext方法创建一个新的空白 Context,而不是通过SecurityContextHolder.getContext获取,是为了避免多线程之间的资源竞争。

如果要获取认证信息:

SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
String name = authentication.getName();
Object principal = authentication.getPrincipal();
log.info("name:{},principal:{}", name, principal);
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();

默认情况下, SecurityContextHolder 使用 ThreadLocal 来存储 SecurityContext。如果有特殊需要,可以通过在启动时配置 SecurityContextHolder 的模式来改变这一点。

SecurityContext

SecurityContext被包含在SecurityContextHolder中,它里边包含一个Authentication对象。

Authentication

Authentication 接口在 Spring Security 中主要有两个用途:

  • 作为 AuthenticationManager 的输入,以提供用户用于认证的凭证。在此场景下, isAuthenticated() 返回 false 。

  • 表示当前已认证的用户。您可以从 SecurityContext 中获取当前 Authentication 。

Authentication 包含:

  • principal :标识用户。当使用用户名/密码进行认证时,这通常是 UserDetails 的一个实例。

  • credentials : 通常是一个密码。在许多情况下,用户认证完成后会清除该信息,以确保不会泄露。

  • authorities : GrantedAuthority 实例是用户被授予的高级权限。两个例子是角色和作用域。

它还配备了一个 AdditionalRequiredFactorsBuilder ,允许你修改现有的 Authentication 实例,并可能将其与另一个实例合并。这在某些场景中非常有用,例如从一个认证步骤(如表单登录)获取权限,并将其应用到另一个认证步骤(如一次性令牌登录)中,如下所示:

Authentication lastestResult = authenticationManager.authenticate(authenticationRequest);
Authentication previousResult = SecurityContextHolder.getContext().getAuthentication();
if (previousResult != null && previousResult.isAuthenticated()) {
    lastestResult = lastestResult.toBuilder()
            .authorities((a) -> a.addAll(previous.getAuthorities()))
            .build();
}

GrantedAuthority

GrantedAuthority 实例是用户被授予的高级权限。两个例子是角色和作用域。

你可以从 Authentication.getAuthorities() 方法中获取 GrantedAuthority 实例。此方法提供一个 Collection 的 GrantedAuthority 对象集合。一个 GrantedAuthority ,不出所料,是授予主体的权限。这些权限通常是“角色”,例如 ROLE_ADMINISTRATOR 或 ROLE_HR_SUPERVISOR 。这些角色之后会被配置用于 Web 授权、方法授权和领域对象授权。Spring Security 的其他部分会解释这些权限,并期望它们存在。当使用基于用户名/密码的认证时, GrantedAuthority 实例通常由 UserDetailsService 加载。

AuthenticationManager

AuthenticationManager 是定义 Spring Security 的 Filter 如何执行认证的 API。由调用 AuthenticationManager 的控制器(即 Spring Security 的 Filters 实例)返回的 Authentication 将被设置到 SecurityContextHolder 中。如果你不与 Spring Security 的 Filters 实例集成,你可以直接设置 SecurityContextHolder ,而无需使用 AuthenticationManager 。

虽然 AuthenticationManager 的实现可以是任何内容,但最常见的实现是 ProviderManager 。

ProviderManager

ProviderManager 是 AuthenticationManager 最常用的实现。 ProviderManager 会委托给一组 AuthenticationProvider 实例。每个 AuthenticationProvider 都有机会表明认证应该成功、失败,或者无法做出决定,从而允许下游的 AuthenticationProvider 来决定。如果配置的 AuthenticationProvider 实例中没有能够认证的,认证将失败并抛出一个 ProviderNotFoundException ,这是一个特殊的 AuthenticationException ,表示 ProviderManager 没有配置来支持传入的 Authentication 类型。

providermanager

实际上,每个 AuthenticationProvider 都知道如何执行特定类型的认证。例如,一个 AuthenticationProvider 可能能够验证用户名/密码,而另一个可能能够认证 SAML 断言。这使得每个 AuthenticationProvider 都可以执行非常具体的认证类型,同时支持多种认证类型,并且只暴露一个 AuthenticationManager bean。

ProviderManager 还允许配置一个可选的父 AuthenticationManager ,当没有 AuthenticationProvider 能够执行认证时,会咨询父 AuthenticationManager 。父 ProviderManager 可以是任何类型的 AuthenticationManager ,但通常是一个 ProviderManager 的实例。

providermanager parent

事实上,多个 ProviderManager 实例可能共享同一个父 AuthenticationManager 。这在存在多个 SecurityFilterChain 实例且它们有一些共同的认证机制(共享的父 AuthenticationManager )的情况下是相当常见的,但它们也可能有不同的认证方式(不同的 ProviderManager 实例)。

providermanagers parent

AuthenticationProvider

你可以将多个 AuthenticationProvider 实例注入到 ProviderManager 中。每个 AuthenticationProvider 都执行一种特定类型的认证。例如, DaoAuthenticationProvider 支持基于用户名/密码的认证,而 JwtAuthenticationProvider 支持验证 JWT 令牌。

使用 AuthenticationEntryPoint 请求凭证

AuthenticationEntryPoint 用于发送一个 HTTP 响应,从客户端请求凭证。

有时,客户端会主动包含凭证(如用户名和密码)来请求资源。在这种情况下,Spring Security 不需要发送一个从客户端请求凭证的 HTTP 响应,因为它们已经包含在内。

在其他情况下,客户端会向其无权访问的资源发起未认证的请求。在这种情况下,会使用 AuthenticationEntryPoint 的实现来向客户端请求凭证。 AuthenticationEntryPoint 的实现可能会将请求重定向到登录页面,返回一个 WWW-Authenticate 头,或者采取其他操作。

AbstractAuthenticationProcessingFilter

AbstractAuthenticationProcessingFilter 用作验证用户凭证的基础 Filter 。在验证凭证之前,Spring Security 通常通过 AuthenticationEntryPoint 请求凭证。

接下来, AbstractAuthenticationProcessingFilter 可以验证提交给它的任何身份验证请求。

abstractauthenticationprocessingfilter

  1. 当用户提交其凭证时, AbstractAuthenticationProcessingFilter 会根据 HttpServletRequest 创建一个 Authentication 用于认证。所创建的 Authentication 类型取决于 AbstractAuthenticationProcessingFilter 的子类。例如, UsernamePasswordAuthenticationFilter 会从 HttpServletRequest 提交的用户名和密码创建一个 UsernamePasswordAuthenticationToken 。

  2. 接下来, Authentication 被传递到 AuthenticationManager 进行认证。

  3. 如果认证失败,那么失败。

    • SecurityContextHolder 被清空。

    • RememberMeServices.loginFail 被调用。如果未配置 remember me,此操作无任何效果。请参见 rememberme 包。

    • AuthenticationFailureHandler 被调用。请参见 AuthenticationFailureHandler 接口。

  4. 如果认证成功,则 Success。

    • SessionAuthenticationStrategy 被通知有新的登录。请参见 SessionAuthenticationStrategy 接口。

    • SecurityContextHolder 中已认证的 Authentication 会被加载,并将其权限添加到返回的 Authentication 中。

    • 认证信息被设置在 SecurityContextHolder 中。之后,如果你需要保存 SecurityContext 以便在未来的请求中自动设置,必须显式调用 SecurityContextRepository#saveContext 。参见 SecurityContextHolderFilter 类。

    • RememberMeServices.loginSuccess 被调用。如果未配置 remember me,此操作无任何效果。请参见 rememberme 包。

    • ApplicationEventPublisher 发布一个 InteractiveAuthenticationSuccessEvent 。

    • AuthenticationSuccessHandler 被调用。请参见 AuthenticationSuccessHandler 接口。

The End.

参考资料

  • Spring Security 官方文档

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: security spring
最后更新:2026年1月4日

魔芋红茶

加一点PHP,加一点Go,加一点Python......

点赞
< 上一篇

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

COPYRIGHT © 2021 icexmoon.cn. ALL RIGHTS RESERVED.
本网站由提供CDN加速/云存储服务

Theme Kratos Made By Seaton Jiang

宁ICP备2021001508号

宁公网安备64040202000141号