一旦你熟悉了如何配置和启动 Spring Security 应用程序,你可能希望了解 Spring Security 框架背后的实际工作方式。本篇文章就来了解一下 Spring Security 框架的基本架构和身份验证的工作过程。
1. Spring Security 核心组件与架构
1.1 SecurityContextHolder、SecurityContext(安全上下文)
首先,最基本的对象就是SecurityContextHolder
。它存储了应用程序当前 SecuirtyContext
(安全上下文)的详细信息(当前用户的信息,是否已经通过验证,拥有的权限,等等)。在默认的情况下,SecurityContextHolder
使用 ThreadLocal
策略来存储这些信息。所以即使没有将 SecuirtyContext
作为参数显式地传递给方法,也始终可以用于同一线程的方法中,在请求结束后 Spring Security 会自动为你清除线程。
1.2 Authentication
SecurityContext
存储了当前与应用程序交互的用户信息,Spring Security 使用一个 Authentication
对象来表示这些信息。
Authentication 源码:
package org.springframework.security.core; |
1.3 UserDetailsService、UserDetails
在上面的源码中,我们看到 Authentication.getPrincipal()
会返回一个 UserDetails
对象,该对象存储了最详细的用户信息,包含了一些必要的用户信息字段。
UserDetails 源码:
public interface UserDetails extends Serializable { |
UserDetailsService
接口负责加载用户信息,创建 UserDetails
对象。Spring Security 提供了几种常用的实现,但是通常推荐自己实现 UserDetailsService
接口更加灵活。
UserDetailsService 源码:
public interface UserDetailsService { |
1.4 GrantedAuthority
除了用户信息之外,Authentication
还提供了 getAuthorities()
方法。这个方法提供了 GrantedAuthority
对象数组。GrantedAuthority
是授予给用户的权限,这些权限通常是 “角色”。GrantedAuthority
对象通常由 UserDetailsService
加载。
1.5 总结
组件功能总结:
SecurityContextHolder
提供对SecurityContext
对象的访问。SecurityContext
用于保存Authentication
和特定的安全请求信息。Authentication
以 Spring Security 的特定方式表示主体。UserDetails
提供用户的必要信息,以便构建Authentication
对象。UserDetailsService
用于创建UserDetails
对象的接口。GrantedAuthority
表示对应用程序访问的权限。
组件关系总结:
2. Spring Security 身份验证
2.1 AuthenticationManager
在讲 Spring Security 身份验证过程前,我们先了解一下 AuthenticationManager
接口。它是身份验证的核心接口,这个接口只有一个方法,就是对用户信息的认证。Spring Security 同样为我们准备了默认的实现,但是根据业务的不同,还是推荐自己实现接口的认证方法。
AuthenticationManager 源码:
public interface AuthenticationManager { |
2.2 身份验证过程
我们先思考一般的身份验证会有哪些步骤呢?
- 提示用户输入用户名和密码。
- 系统验证用户名和密码是否正确(验证成功)。
- 获取用户的上下文信息(用户的角色列表,等等)。
- 创建用户的安全上下文。
- 用户继续进行其他操作,这些操作受到访问控制机制的保护,该机制根据当前上下文检查操作所需的权限。
以上列表的前四项构成了身份验证过程,我们来了解一下这些过程在 Spring Security 是如何发生的。
- 将用户的输入封装到
UsernamePasswordAuthenticationToken
实例中(Authentication
接口实例)。 - 将这个实例传递给
AuthenticationManager
实例进行验证。 - 验证成功后,
AuthenticationManager
返回一个完全填充的Authentication
实例。 - 安全上下文是通过调用
SecurityContextHolder.getContext().setAuthentication(…)
创建的,传入的是完全填充的Authentication
实例。
这样我们就认为用户已经通过了身份验证。
2.3 身份验证流程图
本篇只对身份验证做了一个大概的了解,之后会继续深入探究身份验证的工作原理。