微服务之间打通用户上下文

微服务之间打通用户上下文

  • 打通上下文步骤
    • 需求:
    • 1、gateway网关登录拦截器:【LoginFilter】
      • 解释:
      • 代码
    • 2、SpringMVC全局处理:【GlobalConfig】
      • 解释:
      • 代码:
    • 3、自定义登录拦截器:【LoginInterceptor】
      • 解释:
      • 代码:
    • 4、创建登录上下文对象:【LoginContextHolder】
      • 解释:
      • 代码:
    • 5、简单对外的Util工具类
      • 解释:
      • 代码:
    • 6、测试

打通上下文步骤

需求:

上下文打通,获取用户登录的唯一标识loginId:就是当前端页面发送请求时,如果我们要获取当前登录用户的loginId,那么需要在 HttpServletRequest 里面拿,很不方便现在我们通过自定义拦截器,来实现随时随地获取当前登录用户的loginId,这个就是微服务之间的上下文打通了。

1、gateway网关登录拦截器:【LoginFilter】

解释:

网关登录拦截器:用于自定义header请求头作用:从 Sa-Token 框架中获取当前登录用户的 loginId,【把它放进请求头中】,传给后端其他服务用。loginId 是 用户标识
header 是 服务间传递信息的载体
这个过程其实是 用户上下文的传递如果不做拦截,下游的每个服务都需要拿到token后,通过 Sa-Token 的工具类 StpUtil.getLoginId() 再解析一次。
如果没做统一拦截,每个服务、每个接口都要重复解析 token比如:------------------------------------------------
想象你是个前台小姐姐(前端),你手上拿着一张门票(token),
你给安保(网关)看了一下,安保说:“你是 VIP 用户123,欢迎~”
现在这个安保要放你进去,就顺手在你胸牌上贴了个条:“loginId: 123”
后面每个部门(子服务)看到你,只需要看你胸口的标签就知道你是谁了,不用每次都回头问安保“诶,这人是谁?

代码

在这里插入图片描述

在这里插入图片描述


/*** 网关的登录拦截器:* 作用:解析token,拿到LoginId(当前微信用户登录的唯一标识),用来做用户上下文打通* GlobalFilter:Spring Cloud Gateway 提供的全局过滤器接口,所有请求都会经过此类** @author lujinhong* @since 2025-04-14*/@Component
@Slf4j
public class LoginFilter implements GlobalFilter {/*** 这是过滤器的主要方法,所有经过网关的请求都会先执行这里的逻辑* 1、把sa-token中的用户登录的唯一标识loginId放到请求头中*/@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 获取当前前端传来的请求头信息ServerHttpRequest request = exchange.getRequest();// 获取一个可变的请求构造器,用于后续修改请求头ServerHttpRequest.Builder mutate = request.mutate();String url = request.getURI().getPath();log.info("LoginFilter url:{}", url);if (url.equals("/user/doLogin")) {// 如果当前请求是登录,直接放行,不拦截,只有登录后,用户才会有loginIdreturn chain.filter(exchange);}SaTokenInfo tokenInfo = StpUtil.getTokenInfo();log.info("LoginFilter.filter.url:{}", new Gson().toJson(tokenInfo));// 获取当前用户的登录标识String loginId = (String) tokenInfo.getLoginId();if (StringUtils.isEmpty(loginId)) {throw new RuntimeException("未获取到用户信息-->loginId");}// 往header放一个 loginIdmutate.header("loginId", loginId);// 构造一个新的 ServerWebExchange,并调用 chain.filter() 继续执行下一个过滤器或最终的业务逻辑Mono<Void> filter = chain.filter(exchange.mutate().request(mutate.build()).build());return filter;}
}

2、SpringMVC全局处理:【GlobalConfig】

解释:

就是在项目启动的时候,添加一个我自己定义的拦截器到拦截器链里面
把自定义的拦截器 LoginInterceptor 注册进 Spring MVC 的拦截链里,让它可以拦截所有进来的请求。每个请求都需要经过 LoginInterceptor 拦截器处理一遍LoginInterceptor 用来拦截登录的header头信息,把loginId 存到ThreadLocal中

代码:

在这里插入图片描述


//@Configuration 用于标注一个类为配置类
@Configuration
public class GlobalConfig extends WebMvcConfigurationSupport {/*** 重写这个方法的目的就是换一个自定义的拦截器,用来拦截header的东西* 问题:请求时,打断点看,没有执行到这个方法,直接执行到 LoginInterceptor 的方法* 回答:这个方法是项目启动的时候就初始化执行一次了,把自定义的 LoginInterceptor 注册到拦截器链中了。*      这个操作是在我们发起请求之前,所以打断点不会执行到这里,项目重新启动就可以看到执行这个方法*/protected void addInterceptors(InterceptorRegistry registry) {// /** 拦截所有请求,每个请求都需要进入到 LoginInterceptor 这个自定义拦截器中registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**");}}

3、自定义登录拦截器:【LoginInterceptor】

解释:

上面的网关拦截器,从 satoken中获取到loginId,然后存到放 header 头中。现在这个拦截器作用就是:
1、通过实现 HandlerInterceptor 接口,重写 preHandle 前置拦截方法,在请求达到 controller 之前进行拦截到 header ,从里面获取到 loginId。
2、然后通过 LoginContextHolder(自己定义的登录上下文对象),把 loginId 存放到 ThreadLocal 中。这个拦截器的作用就是做一些请求的前置处理。添加到 ThreadLocal 中的作用:把loginId 放到 ThreadLocal 里面,保证各线程的 loginId 互相不被其他线程干扰影响

代码:

在这里插入图片描述


/*** 自定义一个登录拦截器,用来拦截登录的header头信息** @author lujinhong* @since 2025-04-14*/
@Component
public class LoginInterceptor implements HandlerInterceptor {/*** 前置拦截器* 执行时机:在请求达到 controller 之前进行拦截处理,一般用于: 登录验证、权限校验、拦截*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 获取请求头的 loginIdString loginId = request.getHeader(SubjectConstants.LOGIN_ID);// 把 loginId 存放到上下文里面,然后上下文类LoginContextHolder,就会把loginId 放到 ThreadLocal 里面,保证各线程的 loginId 互相不被其他线程干扰影响。LoginContextHolder.set("loginId", loginId);return true;}/*** 后置拦截器:Controller 执行完毕,但视图还未渲染时执行,就是数据还没有返回给前端* 一般用于:添加模型数据、封装响应,在数据查询出来还没有返回给前端之前,我们还可以添加一些数据或者做一些操作*/@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {// 暂时用不到,做下解释HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);}/***  请求完全结束后处理:一般用于:记录日志、异常处理、清资源*/@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {// 移除此线程局部变量的值,这里就是移除掉 loginIdLoginContextHolder.remove();}
}

4、创建登录上下文对象:【LoginContextHolder】

解释:

把loginId 放到 ThreadLocal 里面,保证各线程的 loginId 互相不被其他线程干扰影响

代码:

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述


/*** 登录的上下文对象** ThreadLocal解释:* ThreadLocal是java.lang下面的一个类,是用来解决java多线程程序中并发问题的一种途径;* 通过为每一个线程创建一份共享变量的【副本】来保证各个线程之间的变量的访问和修改互相不影响;** ThreadLocal存放的值是线程内共享的,线程间互斥的,主要用于线程内共享一些数据,避免通过参数来传递。** 比如 共享变量num=10 , A线程拿到num,改成100;B线程拿到num,改成200,然后A线程打印num,依然是100,线程之间是互斥的** 线程内共享:比如A线程有多个方法:方法1,方法2;方法3,:方法1把num改成100,方法2把num改成200,然后方法方法3打印num=200** 存储位置:每个线程在执行时,都有一个独立的线程局部存储空间,这个空间是用于存储该线程的线程局部变量(即 ThreadLocal 的副本)的** @author lujinhong* @since 2025-04-14*/public class LoginContextHolder {// 只对当前线程有效,子线程无法访问,线程池更不行。// private static final ThreadLocal<Map<String, Object>> threadLocal = new ThreadLocal<>();// 只适合临时新建的子线程,缺点:线程池中的线程是复用的,容易导致数据泄露private static final InheritableThreadLocal<Map<String, Object>> THREAD_LOCAL = new InheritableThreadLocal<>();// 线程池场景专用: 作用:比如num=10,A线程拿到num=10,B线程拿到num后改成15,当线程切换回A线程时,A线程持有的num依然是10// 实际项目用的一定是这个TransmittableThreadLocal,不过视频演示先用InheritableThreadLocal了解一下// private static final TransmittableThreadLocal<Map<String,Object>> transmitThreadLocal = new TransmittableThreadLocal<>();/*** 将此线程局部变量的当前线程副本中的值设置为指定值。(就是会将当前线程的 ThreadLocal 变量副本的值设置为你传入的指定值)* 当前线程副本中的值是存储在“局部变量”中的,不过这个局部变量是线程局部的,即它只属于当前线程,并且由 ThreadLocal 管理,而不是普通的局部变量* 许多应用程序不需要这项功能,它们只依赖于initialValue()方法来设置线程局部变量的值*/public static void set(String key, Object value) {Map<String, Object> map = getThreadLocalMap();map.put(key, value);}/*** 返回此线程局部变量的当前线程副本中的值。如果这是线程第一次调用该方法,则创建并初始化此福副本。*/public static Object get(String key) {Map<String, Object> map = getThreadLocalMap();Object object = map.get(key);return object;}/*** 获取当前线程的局部变量的值,做判断*/public static Map<String, Object> getThreadLocalMap() {// get() : 返回此线程局部变量的当前线程副本中的值。如果这是线程第一次调用该方法,则创建并初始化此副本。Map<String, Object> map = THREAD_LOCAL.get();if (Objects.isNull(map)){// 保证线程安全map = new ConcurrentHashMap<>();THREAD_LOCAL.set(map);}return map;}/*** 移除此线程局部变量的值*/public static void remove() {THREAD_LOCAL.remove();}/*** 获取当前线程的loginId*/public static String getLoginId(){String loginId = (String)getThreadLocalMap().get("loginId");return loginId;}
}

5、简单对外的Util工具类

解释:

就是定义一个工具类,让其他服务可以调用这个方法获取loginId

代码:

在这里插入图片描述


6、测试

如图,随便在哪里调用该方法都可以获取到loginId。可以随时随地获取到当前登录用户的 loginId了,不需要通过 HttpServletRequest 参数来获取了。这就证明微服务之间的上下文打通了

在这里插入图片描述

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

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

相关文章

Hutool之DateUtil:让Java日期处理变得更加简单

前言 在Java开发中&#xff0c;日期和时间的处理是一个常见问题。为了简化这个过程&#xff0c;许多开发者会使用第三方工具包&#xff0c;如Hutool。Hutool是一个Java工具包&#xff0c;提供了许多实用的功能&#xff0c;其中之一就是日期处理。日期时间工具类是Hutool的核心包…

ES中常用的Query和查询作用,以及SpringBoot使用实例

ES中常用的Query和查询作用&#xff0c;以及 SpringBoot 使用实例 文章目录 ES中常用的Query和查询作用&#xff0c;以及 SpringBoot 使用实例MatchAllQueryTermQueryBoolQueryRangeQueryMatchQueryMultiMatchQueryTermsQueryPrefixQueryWildcardQueryRegexpQueryFuzzyQueryDis…

Flutter 自定义插件基础

1、Flutter插件是什么&#xff1f;官方插件库 在开发Flutter应用过程中会涉及到平台相关接口调用&#xff0c;例如数据库操作、相机调用、外部浏览器跳转等业务场景。其实Flutter自身并不支持直接在平台上实现这些功能&#xff0c;而是通过插件包接口去调用指定平台API从而实现…

极狐GitLab 外部授权控制机制是怎样的?

极狐GitLab 是 GitLab 在中国的发行版&#xff0c;关于中文参考文档和资料有&#xff1a; 极狐GitLab 中文文档极狐GitLab 中文论坛极狐GitLab 官网 外部授权控制 (BASIC SELF) 在高度控制的环境中&#xff0c;访问策略可能需要由外部服务控制&#xff0c;该服务允许基于项目…

Linux系统之----冯诺依曼结构

1.简要描述 冯诺依曼体系结构是现代计算机的基本设计思想&#xff0c;其核心理念是将计算机的硬件和软件统一为一个整体&#xff0c;通过存储程序的方式实现计算。冯诺依曼体系结构的核心思想是通过存储程序实现自动计算&#xff0c;其五大部件协同工作&#xff0c;奠定了现代…

【八股】计算机网络

1 概述 1.1 网络的网络 网络把主机连接起来,而互连网(internet)是把多种不同的网络连接起来,因此互连网是网络的网络。而互联网(Internet)是全球范围的互连网。 1.2 ISP 互联网服务提供商 ISP 可以从互联网管理机构获得许多 IP 地址,同时拥有通信线路以及路由器等联…

基于VS Code 为核心平台的python语言智能体开发平台搭建

以下是基于 VS Code 为核心平台&#xff0c;整合 Node-RED、Gradio、Docker Desktop 的智能体可视化开发平台优化方案&#xff0c;聚焦工具链深度集成与开发效率提升&#xff1a; 一、核心架构设计 #mermaid-svg-f8l9kYPAlJ2TlpGF {font-family:"trebuchet ms",verd…

STM32G0单片机自带RTC

STM32有个自带RTC外设&#xff0c;外接32.768KHz的晶振后可得到相对精确的计时功能。 实测了一个一小时快个1秒多。 1 cubeMX设置了RTC后自动生成的初始化代码如下 static void MX_RTC_Init(void) {/* USER CODE BEGIN RTC_Init 0 *//* USER CODE END RTC_Init 0 */RTC_TimeT…

细说STM32单片机FreeRTOS任务管理API函数及多任务编程的实现方法

目录 一、FreeRTOS任务管理API函数 1、任务管理API函数 2、获取任务的句柄 &#xff08;1&#xff09;函数xTaskGetCurrentTaskHandle() &#xff08;2&#xff09;函数xTaskGetIdleTaskHandle() &#xff08;3&#xff09;函数xTaskGetHandle() 3、单个任务的操作 &a…

星露谷物语 7000+ 大型MOD整合包

衣服美化、家具美化、地图美化、人物肖像美化 全地图装修存档、人物美化、扩展包、环境美化、家具、动植物、通用前置包、新增NPC、功能、服装发饰妆 帽子发型农场小镇美化大型玩法拓展实用功能mod 动漫人物形象MOD 地点/动物/地图/功能/机械/家具/建筑/界面美化/扩展/农场/食谱…

C++ `unique_ptr` 多线程使用

C unique_ptr 多线程使用 一、核心结论 操作同一个 unique_ptr&#xff1a;必须加锁&#xff08;所有权转移是非原子操作&#xff09;访问被管理对象&#xff1a;若对象非线程安全&#xff0c;仍需额外同步独立 unique_ptr 实例&#xff1a;不同线程操作不同实例时无需加锁 二…

Android audio系统六 AudioEffect音效加载

对于Android系统智能硬件设备&#xff0c;音效处理的实现方式有以下几种&#xff1a; AudioEffect – android系统音效处理 优点&#xff1a;纯软件实现&#xff0c;移植调试简单方便 缺点&#xff1a;cpu上运行&#xff0c;容易因为资源竞争而出现卡顿 DSP/ADSP – 数字信号处…

深度学习总结(21)

超越基于常识的基准 除了不同的评估方法&#xff0c;你还应该了解的是利用基于常识的基准。训练深度学习模型&#xff0c;你听不到也看不到。你无法观察流形学习过程&#xff0c;它发生在数千维空间中&#xff0c;即使投影到三维空间中&#xff0c;你也无法解释它。唯一的反馈…

接口自动化测试(二)

一、接口测试流程&#xff1a;接口文档、用例编写 拿到接口文档——编写接口用例以及评审——进行接口测试——工具/自动化框架进行自动化用例覆盖(70%)——输出测试报告 自动化的目的一般是为了回归 第一件事情&#xff1a;理解需求&#xff0c;学会看接口文档 只需要找到我…

Linux上位机开发实践(以MCU小系统入门嵌入式电路)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 一直都主张嵌入式软件工程师&#xff0c;也要会做一点电路设计的工作。哪怕自己做的是嵌入式linux上层开发&#xff0c;一个会硬件设计&#xff0c…

浏览器的存储机制 - Storage

浏览器的存储机制 - Storage 前言一、核心概念与区别二、常用 API1、存储数据&#xff08;setItem(key, value)&#xff09;2、 获取数据&#xff08;getItem(key)&#xff09;3、删除单个数据&#xff08;removeItem(key)&#xff09;4、清空所有数据&#xff08;clear()&…

考研单词笔记 2025.04.18

chance n机会&#xff0c;风险&#xff0c;冒险&#xff0c;可能性&#xff0c;巧合&#xff0c;意外a偶然的&#xff0c;意外的 opportunity n机会&#xff0c;时机 crisis n危机&#xff0c;危急关头 the economic crisis 经济危机 danger n危险&#xff0c;可能性&#…

第三方API——Spring Boot 集成阿里云短信发送功能

目录 一. 创建阿里云OSS服务并获取密钥&#xff0c;开通短信服务 1.1 注册阿里云服务器 1.2 开通短信服务 1.3 创建对象存储OSS服务 1.4 RAM用户授权短信权限 1.5 新增用户并授权用户短信权限 1.6 获取 AccessKey ID 和 AccessKey Secret 二. 创建项目集成短信发送 2.1…

b站PC网页版视频播放页油猴小插件制作

文章目录 前言需求分析实施观察页面起始渲染编码效果展示 总结 前言 新手上路,欢迎指导 需求分析 想要一个简约干净的界面,需要去除推荐栏和广告部分. 想要自由调节视频播放速率,需要在视频控制栏加一个输入框控制视频倍速 实施 观察页面起始渲染 因为要使用MutationObse…

畅游Diffusion数字人(27):解读字节跳动提出主题定制视频生成技术Phantom

畅游Diffusion数字人(0):专栏文章导航 前言:主题定制视频生成,特别是zero-shot主题定制视频生成,一直是当前领域的一个难点,之前的方法效果很差。字节跳动提出了一个技术主题定制视频生成技术Phantom,效果相比于之前的技术进步非常显著。这篇博客详细解读一下这一工作。 …