若依框架改造多租户模式

news2025/7/3 6:45:45

当前使用版本3.6.4
16.20.2 (Currently using 64-bit executable)
https://doc.ruoyi.vip/ruoyi-cloud/document/hjbs.html

一、若依Cloud改为多租户模式

当前使用版本3.6.4,既然要改为多租户模式,多租户重点是什么,数据隔离,那么我们首先要知道若依是如何做数据隔离。
image.png
查看DataScopeAspect数据过滤处理的方式发现
现在的系统模块是根据deptId去做的权限,可以把部门理解为一个公司,就是一个租户的意思,那么其实我们根据deptId字段就可以实现多租户模式,但毕竟不是那么规范可读性不高,那我们只需要增加tenantId字段赋值deptId即可,后续新增的模块均可使用tenant_id字段进行数据的过滤。
1、通过自定义注解DataScope+AOP的方式 通过部门和角色的关系进行的数据过滤
2、那么我们需要对部门和角色进行数据隔离,在sys_dept和sys_role表中增加tenant_id字段
3、登录赋值tenant_id返回在token,存在每次请求的线程中,便于获取
这样好像是可以的哦,那么部门和角色的多租户是可以完成的,下面的代码是如何增加tenant_id。
那我们把角色放开给租户的管理员,那菜单的权限和角色的数据权限我们怎么处理呢?这个不要急,下面我会讲到
第一步首先增加字段,自行修改实体类和Mapper文件

ALTER TABLE sys_role ADD COLUMN `tenant_id` bigint NOT NULL COMMENT '租户ID';
ALTER TABLE sys_dept ADD COLUMN  `tenant_id` bigint DEFAULT NULL COMMENT '租户ID';

第二步修改DataScopeAspect类的dataScopeFilter()方法
image.png
修改后的代码如下

 public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias, String permission)
    {
        StringBuilder sqlString = new StringBuilder();
        List<String> conditions = new ArrayList<String>();

        for (SysRole role : user.getRoles())
        {
            String dataScope = role.getDataScope();
            if (!DATA_SCOPE_CUSTOM.equals(dataScope) && conditions.contains(dataScope))
            {
                continue;
            }
            if (StringUtils.isNotEmpty(permission) && StringUtils.isNotEmpty(role.getPermissions())
                    && !StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission)))
            {
                continue;
            }
            if (DATA_SCOPE_ALL.equals(dataScope))
            {
                sqlString = new StringBuilder();
                conditions.add(dataScope);
                break;
            }
            else if (DATA_SCOPE_CUSTOM.equals(dataScope))
            {
                sqlString.append(StringUtils.format(
                        " OR {}.tenant_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias,
                        role.getRoleId()));
            }
            else if (DATA_SCOPE_DEPT.equals(dataScope))
            {
                sqlString.append(StringUtils.format(" OR {}.tenant_id = {} ", deptAlias, user.getDeptId()));
            }
            else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope))
            {
                sqlString.append(StringUtils.format(
                        " OR {}.tenant_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )",
                        deptAlias, user.getDeptId(), user.getDeptId()));
            }
            else if (DATA_SCOPE_SELF.equals(dataScope))
            {
                if (StringUtils.isNotBlank(userAlias))
                {
                    sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getUserId()));
                }
                else
                {
                    // 数据权限为仅本人且没有userAlias别名不查询任何数据
                    sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias));
                }
            }
            conditions.add(dataScope);
        }

        // 多角色情况下,所有角色都不包含传递过来的权限字符,这个时候sqlString也会为空,所以要限制一下,不查询任何数据
        if (StringUtils.isEmpty(conditions))
        {
            sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias));
        }

        if (StringUtils.isNotBlank(sqlString.toString()))
        {
            Object params = joinPoint.getArgs()[0];
            if (StringUtils.isNotNull(params) && params instanceof BaseEntity)
            {
                BaseEntity baseEntity = (BaseEntity) params;
                baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")");
            }
        }
    }

第三步修改sysLoginService.login(查询到当前部门的tenantId返回)和tokenService.createToken代码,其主要目的是讲tenant_id添加在token中

Long tenantId = loginUser.getSysUser().getTenantId();
claimsMap.put(SecurityConstants.DETAILS_TENANT_ID, tenantId);

那我们如何在代码中获取呢
在SecurityUtils类中增加getTenantId方法

public static Long getTenantId()
{
    return SecurityContextHolder.getTenantId();
}

SecurityContextHolder类中添加
public static Long getTenantId()
{
    return Convert.toLong(get(SecurityConstants.DETAILS_TENANT_ID), 0L);
}
public static void setTenantId(String tenantId) {
set(SecurityConstants.DETAILS_TENANT_ID, tenantId);
}

现在是可以取到了,那在哪里赋值呢在HeaderInterceptor类中这是自定义请求头拦截器,将Header数据封装到线程变量中方便获取,增加代码

SecurityContextHolder.setTenantId(ServletUtils.getHeader(request, SecurityConstants.DETAILS_TENANT_ID));
                AuthUtil.verifyLoginUserExpire(loginUser);
                SecurityContextHolder.set(SecurityConstants.LOGIN_USER, loginUser);
                SecurityContextHolder.set(SecurityConstants.DETAILS_TENANT_ID, loginUser.getSysUser().getTenantId());
                SecurityContextHolder.set(SecurityConstants.DETAILS_USERNAME, loginUser.getSysUser().getUserName());
                SecurityContextHolder.set(SecurityConstants.DETAILS_USER_ID, loginUser.getSysUser().getUserId());

那么,目前为止我们已经能根据tenantId来进行租户数据区分了,也可以获取和返回tenantId了
那我们什么时候生成tenantId呢?修改insertDept方法进行赋值即可,也就是新增部门时,将部门的id赋值给tenantId即可。
需要进行权限过滤需要使用该注解。需要自行修改代码,roleService的checkRoleNameUnique,checkRoleKeyUnique。这样是为了使角色独立
image.pngimage.png
根据查看菜单的权限是根据当前角色的权限去拿所以不需要增加tenant也可区分,列如,使用管理员账户增加一个角色,租户管理员角色,配置菜单,这个角色只能配置当前角色下的菜单。
但是每一个租户都有不同的角色分配数据权限时,一定要把全部数据权限禁用掉,不然数据就会串。
image.png
后续增加的业务模块 新增修改查询一定要增加tenantId字段噢。这样就修改完成了。

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

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

相关文章

​【JS重点知识04】JS执行机制(重点面试题)

学前案例&#xff1a; console.log(111); setTimeout(function () {console.log(222); }, 1000) console.log(333); //输出结果&#xff1a;1111 333 222 console.log(111); setTimeout(function () {console.log(222); }, 0) console.log(333); //输出结果&#xff1a;111 33…

各行业万能预约门店小程序源码系统 支持多门店预约 前后端分离 带完整的安装源码包以及搭建教程

在移动互联网快速发展的今天&#xff0c;小程序以其轻量级、即用即走的特点&#xff0c;成为了企业连接用户、提供服务的重要桥梁。特别是在服务行业&#xff0c;如美容美发、餐饮、医疗、教育等领域&#xff0c;门店预约系统的重要性不言而喻。为了满足各行业对门店预约系统的…

C语言实现map数据结构 key—value对应

1.首先43行 createKeyValuePair(char*key ,int value)这个函数就是给一个keyValuePair *pair的指针来通过内存分配将数据key和value存入这个pair指针所对应的内存空间 2.52行freeKeyValuePair这个函数是释放内存空间 3.头文件 struct结构体KeyValuePair就是一个指针一个值 4…

node-mysql的批量插入

此前我批量插入都是用类似这样的命令&#xff1a; sqlcmdinsert into table(field1,field2,...) values ? indata[["f1v1","f2v1"],["f1v2","f2v2"],...] mysqlconn.query(sqlcmd,[indata],(err,res)>{...})但是感觉不太舒服&…

Puffer Finance 如何凭借“再质押”,化解以太坊节点运行难题?

去中心化、DeFi 增强、全链化等 LST 赛道竞争背景下的市场争夺热点频现&#xff0c;以太坊该如何化解节点中心化的现实困境&#xff1f;一起来看看 TinTin AMA 第 10 期活动看看原生流动性再质押平台 Puffer Finance 的技术解决方案&#xff0c;以再质押理念优化用户参与项目流…

银河麒麟V10_系统如何自定义添加桌面右键菜单选项

本篇博客取自《银河麒麟桌面操作系统软件适配常见问题指导手册》官网可以下载。 环境 系统版本 适用系统&#xff1a;V10&#xff08;SP1&#xff09;适用架构&#xff1a;X86、ARM、MIPS 其他版本和架构可做参考。 解决方案 使用下面的这个demo 编译就可以看到效果 peony…

基于jeecgboot-vue3的Flowable流程-已办任务(二)

因为这个项目license问题无法开源&#xff0c;更多技术支持与服务请加入我的知识星球。 接上一节&#xff0c;这个部分主要讲功能代码 1、注册列表数据显示 //注册table数据const { prefixCls, tableContext, onExportXls, onImportXls } useListPage({tableProps: {title: …

2559. 统计范围内的元音字符串数(前缀和) o(n)时间复杂度

给你一个下标从 0 开始的字符串数组 words 以及一个二维整数数组 queries 。 每个查询 queries[i] [li, ri] 会要求我们统计在 words 中下标在 li 到 ri 范围内&#xff08;包含 这两个值&#xff09;并且以元音开头和结尾的字符串的数目。 返回一个整数数组&#xff0c;其中…

【2024PythonPycharm详细安装教程】

1.打开官网 https://www.python.org/ downloads——>Windows 2.找到 Download Windows installer (64-bit) 下载 3.下载完成双击安装包 勾选Add python.exe to PATH(自动配置系统变量) 点击Install Now&#xff08;默认安装&#xff09; 然后看到安装成功&#xff0…

【权威出版/投稿优惠】2024年社会发展与公共文化国际会议(SDPC 2024)

2024 International Conference on Social Development and Public Culture 2024年社会发展与公共文化国际会议 【会议信息】 会议简称&#xff1a;SDPC 2024 截稿时间&#xff1a;点击查看 大会地点&#xff1a;中国上海 会议官网&#xff1a;www.icsdpc.com 会议邮箱&#x…

中山大学和字节发布「视频虚拟试穿」扩散模型VITON-DiT,一键生成换装后视频!

视频虚拟试穿技术日益受到关注&#xff0c;然而现有的工作局限于将服装图像转移到姿势和背景简单的视频上&#xff0c;对于随意拍摄的视频则效果不佳。最近&#xff0c;Sora 揭示了 Diffusion Transformer (DiT) 在生成具有真实场景的逼真视频方面的可扩展性&#xff0c;可以说…

任务悬赏APP搭建必备功能有哪些?

搭建一个任务悬赏APP&#xff0c;需要具备以下必备功能&#xff1a; 在线发布需求&#xff1a;允许用户或商家在线发布任务需求&#xff0c;如浏览广告等 在线接单功能&#xff1a;用户可以在线接单和抢单&#xff0c;以及接收任务完成提醒 推广功能&#xff1a;商户可以通过…

回收站找不到已删除的文件怎么办?别急,这里有解决方法

在数字化时代&#xff0c;电脑中的数据成为了我们生活和工作的重要部分。无论是珍贵的照片、重要的文档&#xff0c;还是日常的工作文件&#xff0c;我们都希望能够妥善保存。然而&#xff0c;误删除文件的情况时有发生&#xff0c;而当我们急切地打开回收站试图找回这些文件时…

JSON 无法序列化

JSON 无法序列化通常出现在尝试将某些类型的数据转换为 JSON 字符串时&#xff0c;这些数据类型可能包含不可序列化的内容。 JSON 序列化器通常无法处理特定类型的数据&#xff0c;例如日期时间对象、自定义类实例等。在将数据转换为 JSON 字符串之前&#xff0c;确保所有数据都…

Anaconda配置环境

查看存在的环境 conda list创建环境 #创建 名称为python38的python环境 conda create -n python38 python3.8 #激活 conda activate python38 #退出当前环境 conda deactivate安装python包 #安装numpy包 conda install numpy #安装指定版本 conda install numpy1.0.2 #安装指…

关机重启命令

1.shutdown命令 [rootlocalhost ~]#shutdown [选项] 时间 选项&#xff1a; -c&#xff1a;取消前一个关机命令 -h&#xff1a;关机 -r&#xff1a;重启 正确使用关机重启命令&#xff0c;因为服务器多个人使用的情况下&#xff0c;硬盘正在高速运行&#xff0c;当强制性关机…

数据恢复工具推荐:电脑回收站删除的文件怎么恢复?8个回收站恢复软件,收藏!

当文件从电脑的回收站被删除后&#xff0c;许多用户可能认为这些文件已永久丢失。然而&#xff0c;实际上&#xff0c;在数据被新数据覆盖之前&#xff0c;这些删除的文件仍然可以通过使用专门的数据恢复软件来恢复。本文将介绍8款顶级的文件恢复软件&#xff0c;恢复电脑回收站…

Mybatis-Plus被恶意CVE一事

前言 Mybatis-Plus框架在2024年5月被人在CVE网络安全漏洞库上提交了漏洞&#xff0c;该漏洞可笑无比&#xff0c;但是有趣的是竟然还是被CVE审核确认为了SQL注入漏洞&#xff1b; 发生了什么 见Mybatis-plus发出的申明公告&#xff1a;https://mp.weixin.qq.com/s?__bizMzA…

【UE5:CesiumForUnreal】——从地球全景聚焦到某区域的动画制作

目录 1.添加Render Texture并和SceneCapture2D关联 1.1 场景准备 1.2 添加Render Texture 1.3 添加SceneCapture2D并关联 2.在Widget上显示Render Texture 2.1 创建Widget 2.2 配置Widget 2.3 添加控制按钮 2.4 添加窗口逻辑 3.制作Sequencer动画 3.1 创建Sequencer…

欧美北美南美国外媒体投稿和东南亚中东亚洲媒体海外新闻发稿软文推广营销策略有哪些?

在当今全球化的浪潮中&#xff0c;中国品牌正积极拓展海外市场&#xff0c;寻求更广阔的发展空间。面对国际竞争&#xff0c;有效的海外媒体发稿营销策略对于品牌国际化至关重要。以下是一些关键点和建议&#xff0c;以帮助品牌在海外市场取得成功。 深入了解目标市场&#xf…