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

我昨天遇到一个问题,就是我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() {@Overridepublic 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<>();//将空值置为nullSpringBeanUtil.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.pswp.cn/news/907025.shtml

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

相关文章

[Java实战]Spring Boot整合达梦数据库连接池配置(三十四)

[Java实战]Spring Boot整合达梦数据库连接池配置&#xff08;三十四&#xff09; 一、HikariCP连接池配置&#xff08;默认&#xff09; 1. 基础配置&#xff08;application.yml&#xff09; spring:datasource:driver-class-name: dm.jdbc.driver.DmDriverurl: jdbc:dm://…

【MySQL】08.视图

视图就是一个由查询到的内容定义的虚拟表。它和真实的表一样&#xff0c;视图包含一系列带有名称的列和行数据。视图的数据变化会影响到基表&#xff0c;基表的数据变化也会影响到视图。 1. 基本使用 mysql> select * from user; -------------------- | id | age | name …

4 通道1250MSPS‐16bit DAC 回放板

简介 4 通道1250MSPS‐16bit DAC 回放板 是一款4 路转换速率最高为1250MSPS 的DAC 回放板&#xff0c; DAC 位数16bit&#xff1b;板卡支持触发输出/触发输入&#xff1b;DAC 采样时钟源支持内部参考时 钟、外部参考时钟、外部采样时钟三种方式&#xff0c;可通过SPI 总线实现时…

C/C++---类型转换运算符

在C中&#xff0c;类型转换运算符&#xff08;Type Conversion Operator&#xff09;允许类对象在需要时自动转换为其他类型。这是一种特殊的成员函数&#xff0c;能够增强类的灵活性和交互性。 1. 基本语法 类型转换运算符的声明格式为&#xff1a; operator 目标类型() co…

大模型Agent

手撕 Agent 1、功能描述 设计一个 Agent,自动选择使用以下工具回答用户的问题: 查看目录下的文件基于给定的文档回答用户问题查看与分析 Excel 文件撰写文档调用 Email 客户端发邮件2、演示用例 实验中使用三个文档演示 Agent 的能力 ./data|__2023年8月-9月销售记录.xlsx…

超临界机组协调控制系统建模项目开发笔记

超临界机组协调控制系统建模项目开发笔记 项目概述 本项目基于两篇论文的方法&#xff0c;对超临界机组协调控制系统进行数据驱动建模。主要使用LSTMTransformer混合架构&#xff0c;实现对主蒸汽压力(Pst)、分离器蒸汽焓值(hm)和机组负荷(Ne)的预测。同时&#xff0c;通过消…

mysql安全管理

数据库管理系统用于管理数据库服务器的各种数据库资源&#xff0c;MYSQL是一个支持多用户的数据库管理系统&#xff0c;实现多用户下&#xff0c;各种数据库资源的安全访问控制&#xff0c;确保数据库资源安全访问成为了数据库管理系统的核心功能。MYSQL安全管理是指允许合法账…

QT中常用的类

Qt 是一个功能强大的跨平台框架&#xff0c;提供了丰富的类库来开发 GUI 和应用程序。以下是 ​​Qt 中常用的核心类​​&#xff0c;按模块分类整理&#xff1a; ​​1. GUI 和窗口管理​​ 类名用途示例场景​​QWidget​​所有 GUI 控件的基类&#xff08;按钮、窗口等&…

【Redis原理篇】五大基本数据类型的底层编码方式

上文&#xff1a;redis底层数据结构 String底层结构 一、编码方式 1.int编码 **适用范围&#xff1a;**64位整数&#xff08;long&#xff09; **实现&#xff1a;**直接将数据存储在redisObject的ptr指针位置。 内存布局&#xff1a; 2.embstr编码 **适用条件&#xf…

自动编码器 潜在空间 Autoencoders 视频截图

【双语】Autoencoders_哔哩哔哩_bilibili 【双语】Autoencoders_哔哩哔哩_bilibili

ZLG USBCANFD python UDS刷写脚本

文章目录 概述python UDS 刷写脚本UI交互界面概述 在实际工作中,有使用周立功的UDSCANFD设备,用来收发CAN数据和UDS on CAN的诊断测试或者UDS on CAN的ECU升级。上位机使用ZCANPro,软件自带ECU刷新界面,可以编辑UDS服务,加载升级文件等,能用是能用,但是仍不能满足一些特…

线程安全问题的成因

前言 大家晚上好呀~~ 今天学习了线程不安全问题的成因。线程安全问题是十分重要的知识点&#xff0c;我想把我所学的与大家分享一波&#xff0c;希望可以帮助到有需要的人&#xff0c;同时加深自己对于线程安全问题的理解。 分析过程如下 结语 今天心情还不错~ 要坚持持续…

C++可变参数宏定义语法笔记

1. 基础语法 定义格式&#xff1a; #define MACRO_NAME(fixed_args, ...) macro_body#define LOG(fmt, ...) printf(fmt, __VA_ARGS__) LOG("Value: %d, Name: %s", 42, "Alice"); // 展开为 printf("Value: %d, Name: %s", 42, "Alice&q…

mongodb安装启动

这里写自定义目录标题 安装包下载安装后文件目录列表bin目录下 mongod 可以对mongodb 进行启动等操作 环境变量配置创建数据目录&#xff0c;日志目录启动参数配置启动 mongodbweb页面访问&#xff0c;检查服务启动是否正常可视化工具连接mongodb创建 database创建collection查…

Leetcode 3562. Maximum Profit from Trading Stocks with Discounts

Leetcode 3562. Maximum Profit from Trading Stocks with Discounts 1. 解题思路2. 代码实现 题目链接&#xff1a;3562. Maximum Profit from Trading Stocks with Discounts 1. 解题思路 这一题没有搞定&#xff0c;思路上整体走偏了&#xff0c;看了一下别人的解答&…

【Redis】第2节|Redis基本数据类型

一、基础数据结构 1. String&#xff08;字符串&#xff09; 特点&#xff1a;二进制安全&#xff0c;支持字符串、数值存储&#xff0c;原子性操作。核心操作&#xff1a; SET key value # 存储键值对 GET key # 获取值 INCR key # 数值…

用matlab提取abaqus odb文件中的节点信息

在MATLAB中提取Abaqus ODB文件中的节点信息&#xff0c;可以通过以下几种方法实现&#xff1a; 方法1&#xff1a;使用MATLAB的ABAQUS Interface工具箱 https://wenku.csdn.net/answer/77axwtqnys 可以参考这个 MATLAB的ABAQUS Interface工具箱提供了直接读取ODB文件的功能。…

【Java】异常处理

1.异常的概念 在程序运行时&#xff0c;打断正常程序流程的不正常情况分两类: 1.错误(Error)&#xff1a;应用程序无法捕获的严重问题(自己无法处理) 例&#xff1a; 虚拟机相关的问题&#xff0c;如虚拟机崩溃、动态链接失败、低层资源错误等 总是不受编译器检查的&#xff0…

Linux(Centos 7.6)命令详解:tar

1.命令作用 命令tar将许多文件一起保存到单个磁带或磁盘存档中&#xff0c;并且可以从存档中恢复单个文件(GNU tar saves many files together into a single tape or disk archive, and can restore individual files from the archive.)。 2.命令语法 Usage: tar [OPTION.…

企业网络综合实训

企业网络综合实训 任务描述&#xff1a; 公司的中心机房、办公区一和办公区二位于同一园区。要求各大楼之间要互通&#xff0c;并且均能访问Internet&#xff1b;同时公司业务需要对外拓展&#xff0c;需要在Internet数据中心机房部署一台对外提供DNS和Web站点服务的服务器。…