章节索引 :

Spring Security 基本认证组件

1. 前言

在前面的章节,我们介绍了 Spring Security 的基础理念。本节开始我们将进入一个新的主题「认证」。在很多应用系统中,认证是用户使用系统的第一步,认证的目的就是系统对用户身份的识别,也就是用户回答系统“我是谁?”,并向系统证明“我就是谁”。

认证的概念不难理解,可实现起来却没那么容易,因为相互信任本身就是一件困难的事。在计算机领域中,似乎很少能出现绝对的信任,而且验证的复杂度与执行效率往往是相互冲突的。我们往往要在安全性与易用性之间做取舍。像社交网站、电子邮箱这类的应用,一般只要输入用户名密码就可以了,并且不用每次都进行登录。但是如果是安全性要求很高的应用,仅仅有用户名密码就不够了,可能还需要 U-Key、电子证书等等。

面对不同的认证方式,我们的业务系统如何实现快速集成?如何将鉴权过程标准化?多个子业务系统之间如何联动?我的认证结果是否能安全的保存下来?可见想把认证过程标准化,并能适配大多数场景也不是件轻松的事。好在 Spring Security 替我们完成了这一挑战。

本节将重点讨论 Spring Security 的认证基础——核心认证组件。

本节所指组件是 Spring Security 在程序执行过程中的重要单元,也就是我们在开发过程中的常用对象或结构。Spring Security 的运转便是通过这些组件的配合实现的。

2. 组件总览

Spring Security 的认证组件可以分为三个组别:

  1. 存储单元。包括: AuthenticationGrantedAuthoritySecurityContextHolderSecurityContext
  2. 认证管理。包括:AuthenticationManagerProviderManagerAuthenticationProvider、AuthenticationEntryPoint;
  3. 流程管理。AbstractAuthenticationProcessingFilter 及其子类。

具体组件名称及作用如下:

组件名 简述
SecurityContextHolder SecurityContextHolder 用于维护 SpringContext。
SecurityContext SecurityContext 用来存储当前认证用户的信息。
Authentication 维护用户用于认证的信息。
GrantedAuthority 认证用户的权限信息比如角色、范围等等。
AuthenticationManager SpringSecurity 向外提供的用于认证的 API 集合。
ProviderManager AuthenticationManager 的常见实现类
AuthenticationProvider 用于 ProviderManager 提供认证实现
AuthenticationEntryPoint 用于获取用户认证信息
AbstractAuthenticationProcessingFilter 是认证过滤器的基础,用于组合认证流程

3. 各组件详细说明

3.1 存储单元

3.1.1 AuthenticationGrantedAuthority

Authentication 是用户的认证信息,该对象有三个核心属性:

  1. principal。用户的身份信息;
  2. credentials。用户的认证凭据,比如密码,通常情况下,当用户完成认证后,此项内容就会被清空;
  3. authorities。用户的权限,用于更高层次的鉴权功能,通常包括角色、使用范围等信息。该属性基本由 GrantedAuthority 实现。GrantedAuthority 是在前述 Authentication 对象中所指的权限信息。在开发过程中,可以通过 Authentication.getAuthorities() 方法获取。权限信息通常包括角色、范围,或者其他扩展内容。

Authentication 有两个主要作用:

  1. AuthenticationManager 对象提供用于认证的信息载体;
  2. 用于获取某个用户的基本信息。

3.1.2 SecurityContextHolderSecurityContext

  1. SecurityContextHolder 对象是整个 Spring Security 体系的核心,它维护着 SecurityContext 对象。
  2. SecurityContext 对象用于衔接 SecurityContextHolderAuthentication 对象,是对 Authentication 的外层封装。
  3. 在 Spring Security 项目中,SecurityContextHolder 对象是唯一的。

图片描述

图 1: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

ProviderManagerAuthenticationManager 的最常用的实现类,它包含了一系列的 AuthenticationProvider 对象,用以判断认证流程是否完成、认证结构是否成功。

图片描述

图 2:ProviderManager 结构示意

3.2.3 AuthenticationProvider

每个 ProviderManager 可以包含多个 AuthenticationProvider ,每个 AuthenticationProvider 提供一种认证类型,例如:DaoAuthenticationProvider 可以完成「用户名 / 密码」的认证,JwtAuthenticationProvider 用于完成 JWT 方式的认证。

3.2.4 AuthenticationEntryPoint

当一个请求包含的认证信息不全时,比如未认证终端访问受保护资源时,AuthenticationEntryPoint 对象便会发挥其作用,如跳转到登录页面、返回认证要求等。

3.3 流程管理

Spring Security 用安全过滤器管理认证流程,AbstractAuthenticationProcessingFilter 是所有认证过滤器的基类。它完成了以下几项内容:

  1. 当用户提交认证信息,AbstractAuthenticationProcessingFilter 首先从请求信息(例如用户名、密码)中创建 Authentication 对象;
  2. Authentication 对象传递给 AuthenticationManager 对象,用于后续认证;
  3. 如果认证失败,则执行失败流程:
    • 清空 SecurityContextHolder 对象;
    • 触发 RememberMeServices.loginFail 方法;
    • 触发 AuthenticationFailureHandler
  4. 如果认证成功,则执行成功流程:
    • SessionAuthenticationStrategy 登记新的登录;
    • Authentication 对象设置到 SecurityContextHolder 对象中,并将 SecurityContext 对象保持到 Session 中;
    • 调用 RememberMeServices.loginSuccess 方法;
    • ApplicationEventPublisher 发起事件 InteractiveAuthenticationSuccessEvent

4. 小结

本小节主要讲解了Spring Security 的主要组件:
Spring Security 的组件包含了「存储单元」、「认证管理」和「流程管理」三个部分;
Spring Security 的各种认证扩展都是基于这三个部分实现的。
下一节我们将讨论认证中最普遍的一种方式:“用户名密码认证”。