用户上下文透传机制详解

news/2025/10/26 0:13:29/文章来源:https://www.cnblogs.com/xiaochenNN/p/19166173

RuoYi-Cloud 用户上下文透传机制详解

引言

在微服务架构中,用户上下文的传递是一个关键问题。当一个请求在多个微服务之间调用时,如何确保用户身份信息能够正确传递到每个服务中,是实现安全认证和权限控制的基础。RuoYi-Cloud 通过 TransmittableThreadLocal 和 Feign 拦截器的组合,实现了高效的用户上下文透传。

1. 核心组件介绍

1.1 TransmittableThreadLocal

RuoYi-Cloud 使用阿里巴巴开源的 TransmittableThreadLocal 来存储用户上下文信息。相比于标准的 ThreadLocal,TransmittableThreadLocal 能够在父子线程之间传递数据,解决了异步场景下的上下文传递问题。

SecurityContextHolder 中,定义了基于 TransmittableThreadLocal 的线程上下文:

private static final TransmittableThreadLocal<Map<String, Object>> THREAD_LOCAL = new TransmittableThreadLocal<>();

1.2 Feign 拦截器

Feign 是 Spring Cloud 中常用的声明式 HTTP 客户端,用于服务间的调用。为了在 Feign 调用中传递用户上下文,RuoYi-Cloud 实现了自定义的 FeignRequestInterceptor

2. 用户上下文透传实现机制

2.1 上下文存储

用户上下文信息存储在 SecurityContextHolder 中,主要包括:

  1. 用户ID:user_id
  2. 用户名:username
  3. 用户标识:user_key
  4. 登录用户对象:login_user
  5. 角色权限:role_permission

这些信息通过 TransmittableThreadLocal存储,确保在线程间正确传递。

2.2 网关到业务服务的上下文传递

当请求从网关转发到业务服务时,网关已经在请求头中添加了用户信息:

// 设置用户信息到请求
addHeader(mutate, SecurityConstants.USER_KEY, userkey);
addHeader(mutate, SecurityConstants.DETAILS_USER_ID, userid);
addHeader(mutate, SecurityConstants.DETAILS_USERNAME, username);

业务服务通过 HeaderInterceptor 拦截器从请求头中提取用户信息并存储到 SecurityContextHolder 中:

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {if (!(handler instanceof HandlerMethod)) {return true;}SecurityContextHolder.setUserId(ServletUtils.getHeader(request, SecurityConstants.DETAILS_USER_ID));SecurityContextHolder.setUserName(ServletUtils.getHeader(request, SecurityConstants.DETAILS_USERNAME));SecurityContextHolder.setUserKey(ServletUtils.getHeader(request, SecurityConstants.USER_KEY));// ...return true;
}

2.3 Feign 调用中的上下文传递

当业务服务需要通过 Feign 调用其他服务时,FeignRequestInterceptor会自动将当前线程中的用户上下文信息添加到 Feign 请求的头部:

@Override
public void apply(RequestTemplate requestTemplate) {HttpServletRequest httpServletRequest = ServletUtils.getRequest();if (StringUtils.isNotNull(httpServletRequest)) {Map<String, String> headers = ServletUtils.getHeaders(httpServletRequest);// 传递用户信息请求头,防止丢失String userId = headers.get(SecurityConstants.DETAILS_USER_ID);if (StringUtils.isNotEmpty(userId)) {requestTemplate.header(SecurityConstants.DETAILS_USER_ID, userId);}String userKey = headers.get(SecurityConstants.USER_KEY);if (StringUtils.isNotEmpty(userKey)) {requestTemplate.header(SecurityConstants.USER_KEY, userKey);}String userName = headers.get(SecurityConstants.DETAILS_USERNAME);if (StringUtils.isNotEmpty(userName)) {requestTemplate.header(SecurityConstants.DETAILS_USERNAME, userName);}String authentication = headers.get(SecurityConstants.AUTHORIZATION_HEADER);if (StringUtils.isNotEmpty(authentication)) {requestTemplate.header(SecurityConstants.AUTHORIZATION_HEADER, authentication);}// 配置客户端IPrequestTemplate.header("X-Forwarded-For", IpUtils.getIpAddr());}
}

2.4 被调用服务的上下文处理

被调用的服务同样配置了 HeaderInterceptor拦截器,会从请求头中提取用户信息并存储到 SecurityContextHolder 中,从而完成整个调用链路上下文的传递。

3. 完整流程示例

以下是一个完整的用户上下文透传流程:

  1. 用户发起业务请求到网关
  2. 网关进行 JWT 验证,提取用户信息并添加到请求头
  3. 网关将请求转发到业务服务 A
  4. 业务服务 A 的 HeaderInterceptor 从请求头提取用户信息,存储到 SecurityContextHolder
  5. 业务服务 A 通过 Feign 调用业务服务 B
  6. FeignRequestInterceptorSecurityContextHolder中的用户信息添加到 Feign 请求头
  7. 业务服务 B 的 HeaderInterceptor从请求头提取用户信息,存储到 SecurityContextHolder
  8. 业务服务 B 处理请求,通过 SecurityContextHolder 获取用户信息
  9. 业务服务 B 返回结果给业务服务 A
  10. 业务服务 A 返回结果给网关
  11. 网关返回结果给用户

4. 技术优势

4.1 透明性

整个上下文传递过程对业务代码是透明的,开发者无需手动处理用户信息的传递。

4.2 异步支持

通过 TransmittableThreadLocal,即使在异步环境下也能正确传递用户上下文。

4.3 安全性

用户信息通过请求头传递,避免了在 URL 或请求体中暴露敏感信息。

4.4 可扩展性

通过配置不同的拦截器和过滤器,可以轻松扩展上下文传递的内容。

5. 配置要点

5.1 Feign 配置

FeignAutoConfiguration中注册了 Feign 拦截器:

@Configuration
public class FeignAutoConfiguration {@Beanpublic RequestInterceptor requestInterceptor() {return new FeignRequestInterceptor();}
}

5.2 拦截器配置

WebMvcConfig中配置了 HeaderInterceptor

@Override
public void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(getHeaderInterceptor()).addPathPatterns("/**").excludePathPatterns(excludeUrls).order(-10);
}

结论

RuoYi-Cloud 通过 TransmittableThreadLocal 和 Feign 拦截器的组合,实现了高效、安全、透明的用户上下文透传机制。这种设计不仅解决了微服务架构中用户身份传递的问题,还保证了在异步环境下上下文的正确传递,为构建安全可靠的微服务系统提供了坚实的基础。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/946520.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

品牌故事不会写?这个AI指令可能帮你解决大问题

本文分享了一个专业的品牌故事生成AI指令,帮助创业者和品牌负责人解决品牌叙事难题。文章提供了基础版和进阶版两个完整指令模板,通过结构化的方式指导AI生成具有情感共鸣和传播价值的品牌故事。内容包含实际使用案例…

电梯调度编程结对项目总结

电梯调度编程结对项目总结 项目地址:https://gitee.com/surrealgit/elevator_surreal.git 一、项目背景 本次课程大作业要求我们以结对编程的形式,完成一个电梯调度算法系统,包括调度算法设计、可视化界面实现、模块…

GuessGame两个版本的区别

GuessGame 两个版本的区别分析目录GuessGame 两个版本的区别分析一、控制台输出二、图形界面2.1 抽象类的好处三、两者对比四、总结 一、控制台输出 控制台版本的ConsoleGame是最基础、最传统的程序交互方式,它通过标…

第二次作业--田佳吉

https://gitee.com/tian-jiaji/qunqing.git

电脑频繁卡顿?4个CMD命令揪出后台隐藏进程

电脑频繁卡顿?4个CMD命令揪出后台隐藏进程电脑频繁卡顿?4个CMD命令揪出后台隐藏进程 你以为电脑卡顿只是垃圾太多?警惕!70%的后台异常进程都藏着“数字内鬼”——可能是黑客植入的隐蔽程序,正偷偷上传你的代码、文…

Graphiti:为智能体构建实时知识图谱,引领更聪明的 AI 时代

我一直觉得很神奇:大多数 AI 系统只要你一停止和它们对话,就会把一切都忘光。你喂给它们 facts、context、chat logs——会话一结束,噗的一下,全没了。这一直是“intelligent” agents 的最大瓶颈。 后来我发现了 …

2025_软件工程师课程辅导

第一周 课程内容 核心教学内容 App Inventor入门实践 使用工具App Inventor网站:http://ai2.17coding.net/AI伴侣手机应用应用相关素材从百度网盘下载上述工具软件和应用素材,如下图所示:课程案例Hello,小蜜蜂!画…

初学Claude Skills/Agents/Plugins功能

初学Claude Skills/Agents/Plugins功能目录SkillsSkills 的关键特点Skills的结构建立不同的 Skills个人 Skills项目 Skills插件 Skills编写 SKILL创建SKILL.md 文件Skills 如何工作学习网址AgentsPlugins Anthropic 刚…

《《《es相关

es 修改思路 一:新建索引 1.新建立索引 addIndex 为ik_smart 二: es 查询 1.es分页是从0开始.分页开始页-1,和结束页-1 2.addIndex 为 ik_smart(分词没有那么细,一般情况可以用这个)每天多努力一点,你将会变得更…

人资新手必看,企业绩效的意义

绩效考核管理是牵引整个企业行动方向的关键 其意义在于: 一、是有利于人事决策,通过绩效考核识别核心人才,对其加以培训、委以重任,同时能够根据员工特长决定人员调配和升降。 二、是确定合理的薪酬水平。根据员工的…

初学Claude Skills Agents功能

初学Claude Skills Agents功能目录SkillsSkills 的关键特点Skills的结构建立不同的 Skills个人 Skills项目 Skills插件 Skills编写 SKILL创建SKILL.md 文件Skills 如何工作学习网址Agents Anthropic 刚刚为 Claude 推出…

题解:P14309 【MX-S8-T2】配对

前言:考场上看出来了某关键性质结果发现做不下去了,然后就寄了。最后因为代码全部加了文件操作荣获总分 0 分的优异成绩。这种题需要我们多加猜测性质并辅以证明。 性质 #1 我们先不考虑任何修改操作。 一个子树内的…

HuggingFace 库使用小技巧

有时在处理数据的时候,我们在数据处理函数内部添加调试语句但是却没有输出,这可能是因为:数据被缓存了,令overwrite_cache: true即可,这样就会重新调用数据处理函数处理数据(一般在调试的时候使用)

【ArcMap】复制选中的线并将其上移一段距离

首先选中需要复制的线 在python2窗口中输入以下代码:此处为上移10米对于地理坐标系,使用0.00009度(约10米)对于投影坐标系,使用10米# -*- coding: utf-8 -*- import arcpy import os import sys# 重新加载sys并设…

启动分布式mapreduce的过程以及prompt

打开docker desktop,启动5个节点 用VS Code连接到第一个节点 为每个节点打开SSH服务service ssh start 每个节点运行cd /usr/local/hadoop-3.4.0/sbin && ./stop-all.sh && rm -rf /usr/local/hadoop…

记一次thinkphp3.2项目迁移失败的原因。 is currently unable to handle this request. HTTP ERROR 500

项目迁移到别的服务器后报错如下 This page isn’t working xxxx is currently unable to handle this request. HTTP ERROR 500这个问题是关闭了错误显示,不能看到明显的错误 打开php.ini文件 display_errors = Off …

20232310 2025-2026-1 《网络与系统攻防技术》实验三实验报告

1.实验内容 1.1回答问题 (1)杀软是如何检测出恶意代码的? 将文件二进制代码与病毒库中的已知恶意特征对比,若匹配则判定为威胁。 分析代码结构和行为逻辑,例如检查是否调用危险API、是否存在异常注册表操作或加密…

题解:AT_apc001_h Generalized Insertion Sort

为数不多能瞎搓出来的构造。 题意:给出一棵以 \(0\) 为根的树和每个点上的值 \(a_i\),每次可以对一个点 \(u\) 进行以下操作:设从根往下走到 \(u\) 的路径为 \(p_1,p_2,\cdots p_k\),那么令 \(a_{p_i}\leftarrow a…

[SWPUCTF 2024 秋季新生赛]http标头 WP

[SWPUCTF 2024 秋季新生赛]http标头 WP 进入界面,会发现一个黑神话悟空,让我们在发布时间 \(0\) 时 \(0\) 分 \(0\) 秒。 我们打开 Burpsuite 抓包,拦截请求。按照他的要求,修改 Date。 Date:Tue, 20 Aug 2024 00:…

20251025 之所思 - 人生如梦

20251025 之所思1. 参加了公司安排的校招,毕业15年再次回到大学,看着一张张年轻的面庞,突然感觉到自己已经不再年轻,年轻真好,可是时光不能倒流,珍惜时间,让每一天都过得有意义。2. 第一次完成了面试,面试前还…