微服务获取登录用户Id与单体服务下获取用户Id对比(黑马头条Day03)

news2025/4/8 10:08:30

前置声明

        当前前后端分离开发项目中,后端某个请求向具体某个数据库中的多个表插入数据时,经常需要使用到当前登录用户的Id(唯一标识)。在当前用户线程下以实现变量共享,同时为了避免不同用户线程之间操作变量的影响,往往选择ThreadLocal本地线程变量实现线程内变量的共享读取,同时避免不同线程间操作的影响。

         产生第一个问题?我们如何获取用户的线程Id并将其存入到本地线程变量ThreadLocal中?

        http连接是无状态的,当前请求不会共享上一次请求的数据,所以需要结果Cookie、Session或Token一起使用,同时Token结合JWT令牌尝尝用于用户的登录校验,只有通过验证的连接才可以执行后续请求,没有通过登录验证的连接直接拒绝本次请求,提醒用户进行登录授权在用户首次登陆时服务端会利用JWT令牌技术生成Token,并将其一起返回给用户,随后用户访问服务端时携带第一次生成的Token,服务端可以实现用户登陆校验。在生成Token的同时可以在payLoad中加入用户Id信息,实现每次连接服务端解析Token获取当前登陆用户Id的目的。

 单体服务(苍穹外卖)如何获取用户Id

实现原理

        苍穹外卖作为一个单体项目,整个项目内只有一个启动类,所有可以认为一个连接就是一个线程,项目结构如下:

        在单体项目中获取用户Id并将其存入ThreadLocal变量中可以在项目拦截器中的以实现,具体流程如下:

        在生成JWT Token的过程中返给UserId,在拦截请求验证过程中解析Token获取用户Id。

实现步骤

  • 创建ThreadLocal类,主要功能包括设置UerId,获取UserId,清除UserId。
public class BaseContext {

    public static ThreadLocal<Long> threadLocal = new ThreadLocal<>();

    /**
     * 设置UserId值
     * @param id
     */
    public static void setCurrentId(Long id) {
        threadLocal.set(id);
    }

    /**
     * 获取UserId值
     * @return
     */
    public static Long getCurrentId() {
        return threadLocal.get();
    }

    /**
     * 请求局部变量ThreadLocal中的UserId,防止内存溢出
     */
    public static void removeCurrentId() {
        threadLocal.remove();
    }
}
  • 在拦截器中解析Token获取用户Id,并将其存入上面创建的BaseContext类中。 
    /**
     * 校验jwt
     *
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //判断当前拦截到的是Controller的方法还是其他资源
        if (!(handler instanceof HandlerMethod)) {
            //当前拦截到的不是动态方法,直接放行
            return true;
        }

        //1、从请求头中获取令牌
        String token = request.getHeader(jwtProperties.getUserTokenName());

        //2、校验令牌
        try {
            log.info("jwt校验:{}", token);
            Claims claims = JwtUtil.parseJWT(jwtProperties.getUserSecretKey(), token);
            Long userId = Long.valueOf(claims.get(JwtClaimsConstant.USER_ID).toString());
            log.info("当前员工id:{}", userId);

            // 将当前登陆ID放入ThreadLocal中
            BaseContext.setCurrentId(userId);
            //3、通过,放行
            return true;
        } catch (Exception ex) {
            //4、不通过,响应401状态码
            response.setStatus(401);
            return false;
        }
    }
  • 上述代码块中主要使用的如下:直接调用静态方法setCurrentIdUserId添加到创建的ThreadLocal中。

 

  • 此后直接从BaseContext中使用getCurrentId(...)获取当前登陆用户的Id。 

微服务(黑马头条)如何获取用户Id

实现原理

        相信做过黑马头条这个项目的同伴都不会陌生微服务这个概念,在黑马头条这个微服务项目中涉及到多个微服务,如:网关微服务、文章微服务、用户微服务......。各个微服务均有属于各自的启动类,各个微服务通过阿里开源nacos实现服务注册与分发,在这个项目中自媒体网关微服务自媒体微服务通过nacos将其关联起来,自媒体相关请求到达时首先通过Nginx方向代理将其分配到自媒体网关微服务接口,对其进行登陆校验(Token解析相关工作),通过验证的请求,nacos会将其分发到自媒体微服务接口,实现后续操作。 两个微服务各自有各自的启动类,二者之间并不直接联系,对应不同的线程。如何使用TheradLocal实现共享变量的线程内部使用。

        首先在自媒体网关微服务中对Token进行解析,并将其存入Header中,随后路由(重置请求)将其发送到自媒体微服务中,后续逻辑同单体服务。

实现步骤

  • 构建ThreadLocal类。
public class WmThreadLocalUtil {
    private static ThreadLocal<WmUser> WM_USER_THREAD_LOCAL = new ThreadLocal<>();

    /**
     * 设置用户信息
     * @param wmUser
     */
    public static void setUser(WmUser wmUser){
        WM_USER_THREAD_LOCAL.set(wmUser);
    }

    /**
     * 获取用户信息
     * @return
     */
    public static WmUser getUser(){
        return WM_USER_THREAD_LOCAL.get();
    }

    /**
     * 清理用户信息
     */
    public static void clear(){
        WM_USER_THREAD_LOCAL.remove();
    }
}
  • 自媒体网关微服务中对登陆进行校验并获取用户信息。 

  • 自媒体微服务中配置拦截器对header进行拦截并进行存储。
@Slf4j
@Component
public class WmTokenInterceptor implements HandlerInterceptor {


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String userId = request.getHeader("userId");
        if(userId != null){
            log.info("将用户信息存入到ThreadLocal中:{}", userId);
            WmUser wmUser = new WmUser();
            wmUser.setId(Integer.valueOf(userId));
            WmThreadLocalUtil.setUser(wmUser);
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
//        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
        log.info("清理ThreadLocal中的用户信息");
        WmThreadLocalUtil.clear();
    }

}

        在上面代码中重写postHandle(...)方法对ThreadLocal中的变量进行及时清除。防止内存溢出。

参考

        苍穹外卖

        黑马头条

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

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

相关文章

SpringCloud(20)之Skywalking Agent原理剖析

一、Agent原理剖析 使用Skywalking的时候&#xff0c;并没有修改程序中任何一行 Java 代码&#xff0c;这里便使用到了 Java Agent 技术&#xff0c;我 们接下来展开对Java Agent 技术的学习。 1.1 Java Agent Java Agent 是从 JDK1.5 开始引入的&#xff0c;算是一个比较老的…

LVS负载均衡集群基础概念

目录 一、集群 1、集群概述 1.1 什么是集群 1.2 集群系统扩展方式 1.2.1 Scale UP&#xff08;纵向扩展&#xff09; 1.2.2 Scale OUT&#xff08;横向扩展&#xff09; 1.2.3 区别 1.3 分布式系统 1.4 分布式与集群 1.5 集群设计原则 1.6 集群设计实现 1.6.1 基础…

【kubernetes】关于k8s集群的pod控制器

目录 一、deployment控制器 二、statefulset控制器 1、验证数据可以持久化 2、验证删除后名称不会改变&#xff0c;数据还会一直存在 3、验证扩容的创建过程是升序串行执行&#xff0c;并且自动创建pv 4、验证滚动更新的时候也是升序执行&#xff0c;数据持久化还在 5、验…

Java面试(1)之 JVM篇

内存模型及原理 1, JVM内存模型 2, 类加载器及双亲委派模型 2.1 类加载器的作用? 将Java文件解析成Class文件对象,即 通过一个类的全限定名来得到其二进制字节流.(不同类加载器加载的对象一定不同) 2.2 什么是双亲委派模型? 如果一个类接收到类加载的请求不会自己去加载,…

Nodejs 第五十二章(定时任务)

什么是定时任务&#xff1f; 定时任务是指在预定的时间点或时间间隔内执行的任务或操作。它们是自动化执行特定逻辑的一种方式&#xff0c;可用于执行重复性的、周期性的或计划性的任务。 定时任务通常用于以下情况&#xff1a; 执行后台任务&#xff1a;定时任务可用于自动…

NLP:spacy库安装与zh_core_web_sm配置

到公司来第一个项目竟然是偏文本信息抽取与结构化的&#xff0c;&#xff08;也太高看我了┭┮﹏┭┮&#xff09; 反正给机会了就上吧&#xff0c;我就一臭实习的&#xff0c;怕个啥。配置了两天的环境&#xff0c;也踩了不少坑&#xff0c;我把我的经历给大家分享一下&#…

FPGA高端项目:FPGA基于GS2971的SDI视频接收+OSD动态字符叠加,提供1套工程源码和技术支持

目录 1、前言免责声明 2、相关方案推荐本博已有的 SDI 编解码方案本方案的SDI接收转HDMI输出应用本方案的SDI接收图像缩放应用本方案的SDI接收纯verilog图像缩放纯verilog多路视频拼接应用本方案的SDI接收HLS图像缩放HLS多路视频拼接应用本方案的SDI接收HLS多路视频融合叠加应用…

弹性地基梁matlab有限元编程 | 双排桩支护结构 | Matlab源码 | 理论文本

专栏导读 作者简介&#xff1a;工学博士&#xff0c;高级工程师&#xff0c;专注于工业软件算法研究本文已收录于专栏&#xff1a;《有限元编程从入门到精通》本专栏旨在提供 1.以案例的形式讲解各类有限元问题的程序实现&#xff0c;并提供所有案例完整源码&#xff1b;2.单元…

微信小程序(五十二)开屏页面效果

注释很详细&#xff0c;直接上代码 上一篇 新增内容&#xff1a; 1.使用控件模拟开屏界面 2.倒计时逻辑 3.布局方法 4.TabBar隐藏复现 源码&#xff1a; components/openPage/openPage.wxml <view class"openPage-box"><image src"{{imagePath}}"…

【算法训练营】:数字三角形,背包问题1,背包问题2(python实现)

数字三角形 时间限制&#xff1a;2 sec 空间限制&#xff1a;256 MB 问题描述 给定一个高度为 n 的“数字三角形”&#xff0c;其中第 i 行&#xff08;1<i<n&#xff09;有 i 个数。&#xff08;例子如下图所示&#xff09; 初始时&#xff0c;你站在“数字三角形”的顶…

leetCode刷题 4.寻找两个正序数组的中位数

目录 1. 思路 2. 解题方法 3. 复杂度 4. Code 题目&#xff1a; 给定两个大小分别为 m 和 n 的正序&#xff08;从小到大&#xff09;数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。 算法的时间复杂度应该为 O(log (mn)) 。 示例 1&#xff1a; 输入&…

SOC设计:关于时钟门控的细节

有如下几个信号 输入信号 1、同步后的rstnsync_clk 2、时钟&#xff1a;clk 3、test_mode 4、软件控制信号&#xff1a;clk_sub_en 输出信号 1、clk_sub 功能&#xff1a;软件配置的使能信号clk_sub_en经过时钟clk 2拍同步处理后产生clk 域下的enable信号&#xff0c;然…

flink重温笔记(十一):Flink 高级 API 开发——flink 四大基石之 Checkpoint(详解存储后端)

Flink学习笔记 前言&#xff1a;今天是学习 flink 的第 11 天啦&#xff01;学习了 flink 四大基石之 Checkpoint &#xff08;检查点&#xff09;&#xff0c;主要是解决大数据领域持久化中间结果数据&#xff0c;以及取消任务&#xff0c;下次启动人可以恢复累加数据问题&…

【LeetCode】升级打怪之路 Day 15:二叉树解题的思维模式 —— 遍历、分解问题

今日题目&#xff1a; 226. 翻转二叉树101. 对称二叉树114. 二叉树展开为链表 目录 LC 226. 翻转二叉树 【easy】LC 101. 对称二叉树 ⭐⭐⭐LC 114. 二叉树展开为链表 ⭐⭐⭐ 今天的题目主要是对二叉树递归遍历的应用&#xff0c;东哥带你刷二叉树&#xff08;思路篇&#xff0…

砝码称重 蓝桥杯

在C中&#xff0c;fabs()和abs()都用于计算数字的绝对值&#xff0c;但它们之间有一些区别。 fabs(double x)&#xff1a;计算浮点数x的绝对值&#xff0c;返回一个double类型的结果。 abs(int x)&#xff1a;计算整数x的绝对值&#xff0c;返回一个int类型的结果。 数组的默…

航天民芯一级代理 MT3608 MT3608L 升压转换器 1.2MHZ

MT3608/MT3608L是恒定频率的6引脚SOT23电流模式升压转换器&#xff0c;适用于小型、低功耗应用。MT3608在1.2MHz&#xff0c;允许使用微小、低成本的频率高度不超过2mm的电容器和电感器。内部软启动可实现较小的浪涌电流和延长电池寿命。MT3608具有自动切换到脉冲的功能轻负载下…

【控制台警告】npm WARN EBADENGINE Unsupported engine

今天用webpack下载几个loader依赖&#xff0c;爆出了三个警告&#xff0c;大概的意思就是本地安装的node和npm的版本不是很匹配&#xff1f; 我的解决思路是&#xff1a; 先检查node和npm版本 然后去官网查找版本的对应 靠&#xff0c;官网404 Node.js (nodejs.org) 就找到…

Conda快速安装的解决方法(Mamba安装)

如果你的Conda安装了&#xff0c;你可能会发现一个问题&#xff0c;就是使用Conda install 安装某个软件时&#xff0c;会特别慢&#xff0c;这时候呢&#xff1f;你会上网去搜&#xff0c;然后大家解决的方法呢。一是告诉你镜像可以下载快一点&#xff0c;二是&#xff0c;Mam…

Sentinel 规则持久化,基于Redis持久化【附带源码】

B站视频讲解 学习链接&#x1f517; 文章目录 一、理论二、实践2-1、dashboard 请求Redis2-1-1、依赖、配置文件引入2-1-2、常量定义2-1-3、改写唯一id2-1-4、新Provider和Publisher2-1-5、改写V2 2-2、应用服务改造2-2-1、依赖、配置文件引入2-2-2、注册监听器 三、源码获取3…

keycloak18.0.0==源码编译打包

参照官方文档building.md mvn -Pdistribution -pl distribution/server-dist -am -Dmaven.test.skip clean install 报错 ---------- [ERROR] Failed to execute goal com.github.eirslett:frontend-maven-plugin:1.12.0:npm (npm-install-common) on project keycloak-theme…