ThreadLocal线程本地变量在dubbo服务使用时候遇到的一个坑

news2025/6/9 11:59:04

我昨天遇到一个问题,就是我springboot项目里面有一个提供代办服务审核的dubbo接口,这个接口给房源项目调用,但是碰到一个问题就是,房源项目每天凌晨5点会查询满足条件过期的数据,然后调用我这边的代办审核dubbo接口,将这个代办任务变成自动拒绝

@Override
@Transactional
public WorkListDataResult auditWorkList(WorkListAuditCmd workListAuditCmd, WorkListLoginUserVo loginUserVo) {
    log.info("auditWorkList WorkListAuditCmd={},WorkListLoginUserVo={}", JSON.toJSONString(workListAuditCmd), JSON.toJSONString(loginUserVo));
    LoginUserUtil.setCurrentUser(loginUserVo);
    WorkListAuditAdapterCmd workListAuditAdapterCmd = AutoMapper.transform(WorkListAuditAdapterCmd.class, workListAuditCmd);
    workListAuditAdapterCmd.setAuditComments(AuditStatusEnum.PASS.getCode().equals(workListAuditCmd.getAuditStatus()) || AuditStatusEnum.PENDING_REEVALUATION.getCode().equals(workListAuditCmd.getAuditStatus()) ? "系统通过" : "系统拒绝");
    workListAuditAdapterCmd.setExtAuditComments(workListAuditAdapterCmd.getAuditComments());
    auditWorkListInternal(workListAuditAdapterCmd);

    WorkListAuditReq workListAuditReq = AutoMapper.transform(WorkListAuditReq.class, workListAuditCmd);
    Boolean needCallback = workListAuditAdapterCmd.getNeedCallback();
    if (needCallback != null && needCallback) {
        //如果dubbo接口里面参数需要回调则回调
        // 注册事务同步器,在事务提交后执行异步任务
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
            @Override
            public void afterCommit() {
                // 确保数据提交后执行异步任务
                CompletableFuture.runAsync(() -> pushAuditResultToBusiness(workListAuditReq), threadPoolExecutor);
            }
        });
    }
    return new WorkListDataResult();
}

这个定时任务调用这个rpc接口时候,第二个参数WorkListLoginUserVo是传的null,这里到后面审核的时候会根据这个 LoginUserUtil.getCurrentUser 判断不为空,就不会进入权限校验的逻辑

protected void auditMultiProcessWorkTaskInternal(WorkListAuditAdapterCmd workListAuditAdapterCmd) {
    log.info("auditMultiProcessWorkTaskInternal WorkListAuditAdapterCmd={}",JSON.toJSONString(workListAuditAdapterCmd));
    //校验是否有审核的
    WorkListRespVo workListInfo = workListQueryService.getWorkListInfoByKeyId(workListAuditAdapterCmd.getWorkListKeyId());

    setAuditInfo(workListAuditAdapterCmd);
    Integer auditStatus = workListInfo.getAuditStatus();
    LoginUser currentUser = Identify.getCurrentUser();
    if(AuditStatusEnum.PENDING_REVIEW.getCode().equals(auditStatus)){
        workListAuditAdapterCmd.setFirstAuditDeptKeyId(currentUser == null ? workListAuditAdapterCmd.getAuditDeptKeyId() : currentUser.getDepartmentKeyId());
        workListAuditAdapterCmd.setFirstAuditUserKeyId(currentUser == null ? workListAuditAdapterCmd.getAuditUserKeyId() : currentUser.getUserKeyId());
        workListAuditAdapterCmd.setAuditStatus(AuditStatusEnum.PASS.getCode().equals(workListAuditAdapterCmd.getAuditStatus()) || AuditStatusEnum.PENDING_REEVALUATION.getCode().equals(workListAuditAdapterCmd.getAuditStatus()) ? AuditStatusEnum.PENDING_REEVALUATION.getCode() : AuditStatusEnum.REFUSE.getCode());
        workListAuditAdapterCmd.setFirstAuditTime(LocalDateTime.now());
    }else {
        workListAuditAdapterCmd.setSecondAuditUserKeyId(currentUser == null ? workListAuditAdapterCmd.getAuditUserKeyId() : currentUser.getUserKeyId());
        workListAuditAdapterCmd.setSecondAuditDeptKeyId(currentUser == null ? workListAuditAdapterCmd.getAuditDeptKeyId() : currentUser.getDepartmentKeyId());
        workListAuditAdapterCmd.setSecondAuditTime(LocalDateTime.now());
        workListAuditAdapterCmd.setFirstAuditTime(null);
        workListAuditAdapterCmd.setFirstAuditDeptKeyId(null);
        workListAuditAdapterCmd.setFirstAuditUserKeyId(null);
    }
    log.info("auditMultiProcessWorkTaskInternal WorkListAuditAdapterCmd={},WorkListRespVo={}",JSON.toJSONString(workListAuditAdapterCmd),JSON.toJSONString(workListInfo));
    // 获取权限代码
    setPermissionCode(workListAuditAdapterCmd, workListInfo);

    //校验是否有审核权限,排除房源定时Job调用的情况
    if(currentUser != null && StringUtils.isNotBlank(currentUser.getUserKeyId()) && StringUtils.isNotBlank(currentUser.getRoleKeyId())){
        checkAuditPermission(currentUser,workListAuditAdapterCmd,workListInfo);
    }
    //更新代办任务数据
    updateWorkList(workListAuditAdapterCmd);
}

生产上面却发现了一个问题,就是有一定概率会有些数据进行了权限校验,也就是currentUser不为空了,后面跟踪房源那边代码才发现,那边调用这个审核的dubbo接口,会调用另外一个查询的rpc接口,

public WorkListDataResult<List<WorkListRespDto>> getWorkListStatusByCondion(WorkListStatusReq req, WorkListLoginUserVo loginUserVo) {
    log.info("getWorkListStatusByCondion WorkListStatusReq={},WorkListLoginUserVo={}", JSON.toJSONString(req), JSON.toJSONString(loginUserVo));
    LoginUserUtil.setCurrentUser(loginUserVo);
    WorkListDataResult<List<WorkListRespDto>> result = new WorkListDataResult<>();
    if (StringUtils.isBlank(req.getApplyType())) {
        result.setFlag(false);
        result.setErrorMessage("待办类型不能为空!");
        return result;
    }
    if (StringUtils.isBlank(req.getPropertyKeyId())) {
        result.setFlag(false);
        result.setErrorMessage("房源ID不能为空!");
        return result;
    }
    List<WorkListRespDto> listResps = new ArrayList<>();
    List<UserByIdOrNumberRpcDto> userList = new ArrayList<>();
    //用于存放用户ID的集合
    List<String> userKeyIdList = new ArrayList<>();
    List<DepartmentByIdOrNoRpcDto> deptList = new ArrayList<>();
    //用于存放部门ID的集合
    List<String> deptIdList = new ArrayList<>();
    //将空值置为null
    SpringBeanUtil.convertEmptyToNull(req);

    WorkListSearchVo workListSearchVo = AutoMapper.transform(WorkListSearchVo.class, req);
    log.info("getWorkListStatusByCondion WorkListSearchVo={}", JSON.toJSONString(workListSearchVo));
    List<WorkListRespVo> list = workListQueryService.getWorkListStatusByCondion(workListSearchVo);
    log.info("getWorkListStatusByCondion list.total=" + list.size());
    if (list != null && list.size() > 0) {
        for (WorkListRespVo viewResp : list) {
            if (viewResp != null) {
                //设置userId和deptId集合
                setDeptOrUserList(viewResp, userKeyIdList, deptIdList);
            }
        }
        if (userKeyIdList != null && userKeyIdList.size() > 0) {
            //批量查询用户信息
            userList = userAndDeptDubboService.queryUserByIdOrNumber(userKeyIdList);
            log.info("getWorkListStatusByCondion userList={}", userList.toString());
        }
        if (deptIdList != null && deptIdList.size() > 0) {
            //批量查询部门信息
            deptList = userAndDeptDubboService.queryByKeyIdOrNo(deptIdList);
            log.info("getWorkListStatusByCondion deptList={}", deptList.toString());
        }
        for (WorkListRespVo viewResp : list) {
            //设置枚举描述
            setWorkListVoDesc(viewResp);
            //设置待办对象用户和部门信息
            setWorkListRespVoDeptOrUser(viewResp, userList, deptList);
            WorkListRespDto workListRespVo = AutoMapper.transform(WorkListRespDto.class, viewResp);
            listResps.add(workListRespVo);
        }
    }
    result.setFlag(true);
    result.setData(listResps);
    return result;
}

这个查询的接口也进行了LoginUserUtil.setCurrentUser(loginUserVo),这段代码会放到ThreadLocal 的线程本地变量里面

而整个这个查询和审核的接口都没有进行这个ThreadLocal线程变量的清除,因为dubbo提供的rpc接口,本质上是使用了线程池技术,会复用一些线程,比如说19号先执行了这个查询代办任务的rpc接口,这个时候设置了currentUser,然后后面调用这个代办审核的接口时候,又刚好拿到了这个19号线程,这个时候通过getCuurentUser拿到的用户就不为空,然后就进入了权限校验的逻辑,解决办法通过自定义dubbo的过滤器,在过滤器里面完成资源的清除

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

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

相关文章

从 0 到 1:Spring Boot 与 Spring AI 深度实战(基于深度求索 DeepSeek)

在人工智能技术与企业级开发深度融合的今天&#xff0c;传统软件开发模式与 AI 工程化开发的差异日益显著。作为 Spring 生态体系中专注于 AI 工程化的核心框架&#xff0c;Spring AI通过标准化集成方案大幅降低 AI 应用开发门槛。本文将以国产大模型代表 ** 深度求索&#xff…

upload-labs通关笔记-第20关 文件上传之杠点绕过

系列目录 upload-labs通关笔记-第1关 文件上传之前端绕过&#xff08;3种渗透方法&#xff09; upload-labs通关笔记-第2关 文件上传之MIME绕过-CSDN博客 upload-labs通关笔记-第3关 文件上传之黑名单绕过-CSDN博客 upload-labs通关笔记-第4关 文件上传之.htacess绕过-CSDN…

Vscode +Keil Assistant编译报错处理

Vscode Keil Assistant编译报错处理 1.报错图片内容 所在位置 行:1 字符: 25 chcp.com 65001 -Command & c:\Users\92170.vscode\extensions\cl.keil-a … ~ 不允许使用与号(&)。& 运算符是为将来使用而保留的&#xff1b;请用双引号将与号引起来(“&”)&…

VSCode C/C++ 开发环境完整配置及一些扩展用途(自用)update:2025/3/31

这里主要记录了一些与配置相关的内容。由于网上教程众多&#xff0c;部分解决方法并不能完全契合我遇到的问题&#xff0c;因此我选择以自己偏好的方式&#xff0c;对 VSCode 进行完整的配置&#xff0c;并记录在使用过程中遇到的问题及解决方案。后续内容也会持续更新和完善。…

Docker系列(二):开机自启动与基础配置、镜像加速器优化与疑难排查指南

引言 docker 的快速部署与高效运行依赖于两大核心环节&#xff1a;基础环境搭建与镜像生态优化。本期博文从零开始&#xff0c;系统讲解 docker 服务的管理配置与镜像加速实践。第一部分聚焦 docker 服务的安装、权限控制与自启动设置&#xff0c;确保环境稳定可用&#xff1b…

a16z:AI带来了全新的9种开发软件的模式

非常有启发的9条新兴模式&#xff0c;推荐给已经上手 vibeCoding 的读者们。 开发者正在将 AI 从简单的工具转变为构建软件的新基础。许多核心概念&#xff0c;如版本控制、模板、文档&#xff0c;甚至用户的定义&#xff0c;都在被重新思考。代理&#xff08;Agent&#xff09…

在 Excel 使用macro————仙盟创梦IDE

Dim filePath As StringDim fileContent As StringDim lines() As StringDim dataArray() As StringDim lineCount As LongDim maxCols As LongDim i As Long, j As Long 文件路径filePath "" 检查文件是否存在If Dir(filePath) "" ThenMsgBox "文件…

鸿蒙devEco studio如何创建模拟器

官网原文链接&#xff1a;https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/ide-emulator-create 操作步骤 点击菜单栏的Tools > Device Manager&#xff0c;点击右下角的Edit设置模拟器实例的存储路径Local Emulator Location&#xff0c;Mac默认存储在~/…

鸿蒙路由参数传递

页面test.ets 代码如下&#xff1a; import router from ohos.router Entry Component struct Test {State message: string Hello WorldState username: string huState password: string 1build() {Row() {Column() {Text(this.message).fontSize(50).fontWeight(FontWe…

springboot 控制层调用业务逻辑层,注入报错,无法自动装配 解决办法

报错&#xff1a; 解决&#xff1a;愿意是业务逻辑层&#xff0c;即service层的具体实现类没有加注解Service导致的&#xff0c;加上解决了&#xff01;&#xff01;

MySQL:11_事务

事务 一.CURD不加控制&#xff0c;会有什么问题&#xff1f; 二.什么是事务&#xff1f; 事务就是一组DML语句组成&#xff0c;这些语句在逻辑上存在相关性&#xff0c;这一组DML语句要么全部成功&#xff0c;要么全部失败&#xff0c;是一个整体。MySQL提供一种机制&#xf…

Linux中的文件系统和软硬连接

磁盘的访问方式 CHS&#xff08;柱面&#xff0c;磁头&#xff0c;扇区&#xff09; 法&#xff08;磁盘硬件查找&#xff09;&#xff1a; 确定柱面&#xff08;C&#xff09; 磁头臂移动到对应的柱面位置。例如&#xff0c;柱面号为 5&#xff0c;则磁头移动到第 5 个磁道组…

Spring AI:Java开发者的AI开发新利器

目录 一、引言 二、Spring AI 是什么 三、核心功能与特性 3.1 统一的 API 抽象 3.2 丰富的模型支持 3.3 低代码集成 3.4 结构化数据输出 3.5 流式数据响应 四、应用场景 4.1 智能客服系统 4.2 图像识别应用 4.3 数据分析与预测 五、快速上手 5.1 环境搭建 5.2 创…

Spring Cloud Sleuth与Zipkin深度整合指南:微服务链路追踪实战

上篇文章简单介绍了SpringCloud系列熔断器&#xff1a;Sentinel的搭建及基本用法&#xff0c;今天继续讲解下SpringCloud的微服务链路追踪&#xff1a;Zipkin的使用&#xff01;在分享之前继续回顾下本次SpringCloud的专题要讲的内容&#xff1a; 前置知识说明 在开始本教程前…

spring-ai 集成 mcp 之投机取巧

主旨 这篇文章主旨就一点&#xff0c;罗列spring-ai对mcp集成导致出现的一系列问题 分析 由于mcp未问世之前&#xff0c;就早就已经有了工具调用&#xff0c;源码如下&#xff1a; public interface ToolCallback {/*** Definition used by the AI model to determine when a…

大语言模型的完整训练周期从0到1的体系化拆解

以下部分内容参考了AI。 要真正理解大语言模型&#xff08;LLM&#xff09;的创生过程&#xff0c;我们需要将其拆解为一个完整的生命周期&#xff0c;每个阶段的关键技术相互关联&#xff0c;共同支撑最终模型的涌现能力。以下是体系化的训练流程框架&#xff1a; 阶段一&am…

历年北京邮电大学保研上机真题

2025北京邮电大学保研上机真题 2024北京邮电大学保研上机真题 2023北京邮电大学保研上机真题 在线测评链接&#xff1a;https://pgcode.cn/problem?classification1 32位二进制串加法 题目描述 输入一个32位的二进制01串&#xff0c;输出这个数1和3后的32位二进制串。 输入…

《仿盒马》app开发技术分享-- 定位获取(端云一体)

开发准备 上一节我们实现了地址管理页面的数据查询和展示&#xff0c;接下来我们要实现的功能是地址添加相关的&#xff0c;我们想实现的功能是地图选点&#xff0c;那么在地图选点之前我们要做的就是先获取用户当前的定位。获取定位后我们拿到经纬度和其他信息&#xff0c;然…

黑马点评--基于Redis实现共享session登录

集群的session共享问题分析 session共享问题&#xff1a;多台Tomcat无法共享session存储空间&#xff0c;当请求切换到不同Tomcat服务时&#xff0c;原来存储在一台Tomcat服务中的数据&#xff0c;在其他Tomcat中是看不到的&#xff0c;这就导致了导致数据丢失的问题。 虽然系…

Mujoco 学习系列(二)基础功能与xml使用

这篇文章是 Mujoco 学习系列第二篇&#xff0c;主要介绍一些基础功能与 xmI 使用&#xff0c;重点在于如何编写与读懂 xml 文件。 运行这篇博客前请先确保正确安装 Mujoco 并通过了基本功能与GUI的验证&#xff0c;即至少完整下面这个博客的 第二章节 内容&#xff1a; Mujoc…