Spring Security 基本认证组件
1. 前言
在前面的章节,我们介绍了 Spring Security 的基础理念。本节开始我们将进入一个新的主题「认证」。在很多应用系统中,认证是用户使用系统的第一步,认证的目的就是系统对用户身份的识别,也就是用户回答系统“我是谁?”,并向系统证明“我就是谁”。
认证的概念不难理解,可实现起来却没那么容易,因为相互信任本身就是一件困难的事。在计算机领域中,似乎很少能出现绝对的信任,而且验证的复杂度与执行效率往往是相互冲突的。我们往往要在安全性与易用性之间做取舍。像社交网站、电子邮箱这类的应用,一般只要输入用户名密码就可以了,并且不用每次都进行登录。但是如果是安全性要求很高的应用,仅仅有用户名密码就不够了,可能还需要 U-Key、电子证书等等。
面对不同的认证方式,我们的业务系统如何实现快速集成?如何将鉴权过程标准化?多个子业务系统之间如何联动?我的认证结果是否能安全的保存下来?可见想把认证过程标准化,并能适配大多数场景也不是件轻松的事。好在 Spring Security 替我们完成了这一挑战。
本节将重点讨论 Spring Security 的认证基础——核心认证组件。
本节所指组件是 Spring Security 在程序执行过程中的重要单元,也就是我们在开发过程中的常用对象或结构。Spring Security 的运转便是通过这些组件的配合实现的。
2. 组件总览
Spring Security 的认证组件可以分为三个组别:
- 存储单元。包括:
Authentication
、GrantedAuthority
、SecurityContextHolder
、SecurityContext
; - 认证管理。包括:
AuthenticationManager
、ProviderManager
、AuthenticationProvider
、AuthenticationEntryPoint; - 流程管理。
AbstractAuthenticationProcessingFilter
及其子类。
具体组件名称及作用如下:
组件名 | 简述 |
---|---|
SecurityContextHolder | SecurityContextHolder 用于维护 SpringContext。 |
SecurityContext | SecurityContext 用来存储当前认证用户的信息。 |
Authentication | 维护用户用于认证的信息。 |
GrantedAuthority | 认证用户的权限信息比如角色、范围等等。 |
AuthenticationManager | SpringSecurity 向外提供的用于认证的 API 集合。 |
ProviderManager | AuthenticationManager 的常见实现类 |
AuthenticationProvider | 用于 ProviderManager 提供认证实现 |
AuthenticationEntryPoint | 用于获取用户认证信息 |
AbstractAuthenticationProcessingFilter | 是认证过滤器的基础,用于组合认证流程 |
3. 各组件详细说明
3.1 存储单元
3.1.1 Authentication
和 GrantedAuthority
Authentication
是用户的认证信息,该对象有三个核心属性:
- principal。用户的身份信息;
- credentials。用户的认证凭据,比如密码,通常情况下,当用户完成认证后,此项内容就会被清空;
- authorities。用户的权限,用于更高层次的鉴权功能,通常包括角色、使用范围等信息。该属性基本由
GrantedAuthority
实现。GrantedAuthority
是在前述Authentication
对象中所指的权限信息。在开发过程中,可以通过Authentication.getAuthorities()
方法获取。权限信息通常包括角色、范围,或者其他扩展内容。
Authentication
有两个主要作用:
- 为
AuthenticationManager
对象提供用于认证的信息载体; - 用于获取某个用户的基本信息。
3.1.2 SecurityContextHolder
和 SecurityContext
SecurityContextHolder
对象是整个 Spring Security 体系的核心,它维护着SecurityContext
对象。SecurityContext
对象用于衔接SecurityContextHolder
和Authentication
对象,是对Authentication
的外层封装。- 在 Spring Security 项目中,
SecurityContextHolder
对象是唯一的。
SecurityContextHolder
对象为 Spring Security 保存着所有认证用户的信息。Spring Security 本身不关心 SecurityContextHolder
如何获取到用户信息,一旦其中包含用户对象,Spring Security 就认为当前用户认证成功了。
由此可知,假如我们希望 Spring Security 保持认证通过状态,而不需要经历复杂的认证过程,最直接的方式便是往 SecurityContextHolder
里写入用户信息。例如:
// 第一步,创建 SecurityContext 对象
SecurityContext context = SecurityContextHolder.createEmptyContext();
// 第二步,为 SpringContext 注入认证信息,例如 用户名:testUser;密码:testPassword;角色:ROLE_TEST
Authentication authentication = new TestingAuthenticationToken("testUser", "testPassword", "ROLE_TEST");
context.setAuthentication(authentication);
// 第三步,为 SecurityContextHolder 注入当前用户的 SecurityContext 对象
SecurityContextHolder.setContext(context);
通过以上代码,在 Spring Security 中加入了一个已认证的用户。
通过 SecurityContextHolder
对象,我们也可以获取到当前登录的用户信息。例如:
// 第一步,获取当前线程内的 SecurityContext 对象
SecurityContext context = SecurityContextHolder.getContext();
// 第二步,从 SecurityContext 对象中获取用户认证信息,如用户名、密码、权限
Authentication authentication = context.getAuthentication();
String username = authentication.getName();
Object principal = authentication.getPrincipal();
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
默认情况下,SecurityContextHolder
使用 ThreadLocal
存储变量信息。
3.2 认证管理
3.2.1 AuthenticationManager
AuthenticationManager
为 Spring 过滤器提供认证支持 API。AuthenticationManager
的实现形式并没有严格限制,通常情况下使用 ProviderManager
。
3.2.2 ProviderManager
ProviderManager
是 AuthenticationManager
的最常用的实现类,它包含了一系列的 AuthenticationProvider
对象,用以判断认证流程是否完成、认证结构是否成功。
3.2.3 AuthenticationProvider
每个 ProviderManager
可以包含多个 AuthenticationProvider
,每个 AuthenticationProvider
提供一种认证类型,例如:DaoAuthenticationProvider
可以完成「用户名 / 密码」的认证,JwtAuthenticationProvider
用于完成 JWT 方式的认证。
3.2.4 AuthenticationEntryPoint
当一个请求包含的认证信息不全时,比如未认证终端访问受保护资源时,AuthenticationEntryPoint
对象便会发挥其作用,如跳转到登录页面、返回认证要求等。
3.3 流程管理
Spring Security 用安全过滤器管理认证流程,AbstractAuthenticationProcessingFilter
是所有认证过滤器的基类。它完成了以下几项内容:
- 当用户提交认证信息,
AbstractAuthenticationProcessingFilter
首先从请求信息(例如用户名、密码)中创建Authentication
对象; - 将
Authentication
对象传递给AuthenticationManager
对象,用于后续认证; - 如果认证失败,则执行失败流程:
- 清空 SecurityContextHolder 对象;
- 触发
RememberMeServices.loginFail
方法; - 触发
AuthenticationFailureHandler
。
- 如果认证成功,则执行成功流程:
SessionAuthenticationStrategy
登记新的登录;- 将
Authentication
对象设置到SecurityContextHolder
对象中,并将SecurityContext
对象保持到 Session 中; - 调用
RememberMeServices.loginSuccess
方法; ApplicationEventPublisher
发起事件InteractiveAuthenticationSuccessEvent
4. 小结
本小节主要讲解了Spring Security 的主要组件:
Spring Security 的组件包含了「存储单元」、「认证管理」和「流程管理」三个部分;
Spring Security 的各种认证扩展都是基于这三个部分实现的。
下一节我们将讨论认证中最普遍的一种方式:“用户名密码认证”。