SecurityContextHolder, SecurityContext和Authentication

本文详细介绍了Spring Security中的核心组件,包括SecurityContextHolder、SecurityContext、Authentication、GrantedAuthority及UserDetails等,并解释了如何利用这些组件进行身份验证和授权。

原创地址:https://blog.youkuaiyun.com/ro_wsy/article/details/44341547 

SecurityContextHolder, SecurityContext和Authentication

SecurityContextHolder是SpringSecurity最基本的组件了,是用来存放SecurityContext的对象,默认是使用ThreadLocal实现的,这样就保证了本线程内所有的方法都可以获得SecurityContext对象。

SecurityContextHolder还有其他两种模式,分别为SecurityContextHolder.MODE_GLOBAL和SecurityContextHolder.MODE_INHERITABLETHREADLOCAL,前者表示SecurityContextHolder对象的全局的,应用中所有线程都可以访问;后者用于线程有父子关系的情境中,线程希望自己的子线程和自己有相同的安全性。

大部分情况下我们不需要修改默认的配置,ThreadLocal是最常用也是最合适大部分应用的。

获得认证主体信息

我们可以用下面的代码段获得认证的主体信息

[java]  view plain  copy
  1. Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();  
  2.   
  3. if (principal instanceof UserDetails) {  
  4.   String username = ((UserDetails)principal).getUsername();  
  5. else {  
  6.   String username = principal.toString();  
  7. }  
第一行代码返回的是一个UserDetails类型的实例,其中包含了username,password和权限等信息,当然,我们也可以通过实现这个接口自定义我们自己的UserDetails实例,给我们自己的应用使用,以符合需要的业务逻辑。

UserDetailsService

上面说到可以自定义UserDetails实例,我们怎么获得这个实例呢,就需要通过UserDetailsService来实现,这个接口只有一个方法

[java]  view plain  copy
  1. UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;  
这个方法接受一个用户名参数,返回UserDetails实例。

认证成功之后,UserDetails对象用来构建Authentication对象并存放在SecurityContextHolder中,所以,我们需要的用户信息都可以通过SecurityContextHolder拿到。

GrantedAuthority

Authentication对象还提供了getAuthorities方法,获取用户被赋予的权限,权限通常对应着角色,什么角色对应着什么样的访问权限,例如ADMIN_ROLE可以访问/admin下的内容,其他角色则无权访问。

GrantedAuthority对象也通常由UserDetailsService实例获取。

总结

我们上面提到了如下几个对象:

SecurityContextHolder:提供对SecurityContext的访问
SecurityContext,:持有Authentication对象和其他可能需要的信息
Authentication:Spring Security方式的认证主体
GrantedAuthority:对认证主题的应用层面的授权
UserDetails:构建Authentication对象必须的信息,可以自定义,可能需要访问DB得到
UserDetailsService:通过username构建UserDetails对象

<think>我们正在讨论Spring Security中的两个核心概念:SecurityContextHolderSecurityContextRepository。 用户已经知道SecurityContextRepository的作用,现在想了解它与SecurityContextHolder的区别。 我们需要明确: 1. SecurityContextHolder的作用:它是保存当前安全上下文的地方,通常使用ThreadLocal来存储,以便在整个请求处理线程中都可以访问。 2. SecurityContextRepository的作用:它是负责在请求之间持久化恢复安全上下文的机制(比如存储在Session中或请求属性中)。 因此,两者的区别主要在于: - SecurityContextHolder:是当前线程中安全上下文的持有者(存储容器)。 - SecurityContextRepository:是安全上下文在请求之间持久化的策略(存储读取的仓库)。 具体阐述: 1. **SecurityContextHolder**: - 作用:提供对当前安全上下文的访问。它使用`ThreadLocal`作为默认策略,将安全上下文绑定到当前执行线程。 - 关键方法:`getContext()`(获取当前上下文)、`setContext(context)`(设置当前上下文)、`clearContext()`(清除当前上下文)。 - 生命周期:通常在一次请求处理期间有效,由`SecurityContextPersistenceFilter`在请求结束时清理(清除ThreadLocal,防止内存泄漏)。 2. **SecurityContextRepository**: - 作用:定义如何加载(`loadContext`)保存(`saveContext`)安全上下文。它决定了安全上下文在何处以及如何存储(例如HTTP Session、请求属性等)。 - 关键方法:`loadContext(HttpRequestResponseHolder)`(从存储介质加载上下文)、`saveContext(SecurityContext, HttpServletRequest, HttpServletResponse)`(保存上下文到存储介质)。 - 生命周期:在请求开始时加载上下文到SecurityContextHolder,在请求结束时将SecurityContextHolder中的上下文保存到存储介质。 协作流程: - 当请求到达时,`SecurityContextPersistenceFilter`会使用`SecurityContextRepository`从存储(如Session)中加载安全上下文,然后将其设置到`SecurityContextHolder`中。 - 在请求处理过程中,应用程序可以通过`SecurityContextHolder`获取当前的安全上下文(例如获取当前认证的用户)。 - 当请求结束时,`SecurityContextPersistenceFilter`会再次调用`SecurityContextRepository`将更新后的安全上下文保存到存储介质,并清除`SecurityContextHolder`(因为线程可能会被线程池回收重用)。 因此,两者的关系是: - `SecurityContextRepository`负责跨请求的持久化。 - `SecurityContextHolder`负责当前请求线程内的访问。 我们可以用以下表格总结: | 特性 | SecurityContextHolder | SecurityContextRepository | |------|------------------------|---------------------------| | **目的** | 提供当前线程对安全上下文的访问 | 管理安全上下文在请求之间的存储恢复 | | **存储位置** | 线程局部变量(ThreadLocal) | 持久化存储(如HTTP Session、请求属性等) | | **主要方法** | `getContext()`, `setContext()`, `clearContext()` | `loadContext()`, `saveContext()` | | **生命周期** | 请求处理期间 | 跨请求 | 注意:在无状态的应用中(如使用JWT),我们可能使用`NullSecurityContextRepository`或者`RequestAttributeSecurityContextRepository`,这样就不会在会话中存储安全上下文,而是每个请求都需要重新认证。此时,`SecurityContextHolder`仍然在请求处理线程中持有当前请求的安全上下文,但不会保存到会话。 生成相关问题: 1. 如何自定义Spring Security中安全上下文的存储策略? 2. SecurityContextHolder的使用场景最佳实践有哪些? 3. 在异步处理中,如何确保SecurityContextHolder的正确传递? 4. 无状态应用(如RESTful API)中,SecurityContextRepository应该如何配置? 回答结构: 1. 先分别介绍两者。 2. 对比它们的职责协作关系。 3. 总结区别。 4. 提出相关问题。 </think>### SecurityContextHolderSecurityContextRepository 的区别 #### 1. **核心职责不同** - **`SecurityContextHolder`** 是**当前线程安全上下文的持有者**,本质上是一个存储容器: ✅ 提供静态方法 `getContext()` 获取当前线程的安全上下文(`SecurityContext`) ✅ 通过 `setContext()` 设置当前线程的上下文 ✅ 使用 `ThreadLocal` 实现线程绑定,确保每个请求独立访问自己的安全上下文[^1] > *示例代码:获取当前认证信息* > ```java > SecurityContext context = SecurityContextHolder.getContext(); > Authentication auth = context.getAuthentication(); // 当前用户认证信息 > ``` - **`SecurityContextRepository`** 是**安全上下文的持久化管理器**,负责跨请求的状态存储: ✅ 定义 `loadContext()` 从存储介质(如 Session)加载上下文 ✅ 通过 `saveContext()` 将上下文保存到存储介质 ✅ 解耦存储逻辑,支持会话存储、无状态存储等不同策略[^2] #### 2. **生命周期与作用域** | **组件** | **作用域** | **生命周期** | |--------------------------|-------------------|---------------------------| | `SecurityContextHolder` | 单次请求内 | 请求开始到结束(线程绑定) | | `SecurityContextRepository` | 跨请求 | 持久化存储(如 Session) | #### 3. **协作关系** 两者通过 **`SecurityContextPersistenceFilter`** 协同工作: 1. **请求开始时**: ```mermaid graph LR A[新请求] --> B[Repository.loadContext] B --> C[Holder.setContext] ``` `Repository` 从存储加载上下文 → `Holder` 绑定到当前线程[^3] 2. **请求结束时**: ```mermaid graph LR D[Holder.getContext] --> E[Repository.saveContext] E --> F[持久化存储] ``` 从 `Holder` 获取更新后的上下文 → `Repository` 保存到存储介质[^3] #### 4. **设计目标对比** | **特性** | SecurityContextHolder | SecurityContextRepository | |-----------------------|--------------------------------|----------------------------------| | **本质** | 上下文容器 | 持久化策略接口 | | **关键操作** | `get/setContext()` | `load/saveContext()` | | **状态存储位置** | 线程局部变量(ThreadLocal) | 外部存储(Session/Redis等) | | **线程安全保证** | 自动隔离各请求线程 | 依赖存储介质实现 | | **扩展性** | 固定实现 | 可自定义实现(如Redis存储) | ### 总结 - **`SecurityContextHolder`** 是**运行时容器**:管理当前请求线程内的安全上下文访问。 - **`SecurityContextRepository`** 是**持久化策略**:解决安全上下文在请求间的存储与恢复问题。 两者共同构成 Spring Security 的**身份状态管理基石**:`Holder` 提供线程内即时访问,`Repository` 保障跨请求状态一致性[^1][^2][^3]。 --- **
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值