前言:
在使用如若依、Jeecg等后台管理系统进行二次开发的时候,我们总会涉及到数据隔离相关的内容,如每个非管理员用户应该都只能看到自己创建的数据,而不是所有的数据,本文将以jeecg为例像大家介绍如何每个用户之间的数据隔离的效果。
先从功能需求说起,当查询房屋列表的时候,每个房东(用户)应该只能看到自己的房屋(house)表数据,而房屋和房东的关系存放于房东(house_holder)表中,所以我们需要通过两个表的关联来实现数据隔离的查询。而jeecg中要如何实现呢?我们一起啦看看吧
基于Jeecg实现:
先放出官方文档中跟数据隔离有关的内容:doc.jeecg.com/2044046
- 进入菜单管理页面,找到想要进行数据隔离的菜单,点击添加一个下级并进行配置
选择按钮/权限并配置菜单路径(笔者能力有限暂时无法弄清菜单路径和授权表示的作用在哪)
然后对添加的这个按钮/权限进行数据规则的配置,
由于我们需要要从另外一张表中去查出跟user_id
有关的house_id
所以要使用自定义SQL的方式
- 在角色管理中对用角色进行数据权限的授权
给需求中的房东用户配置上数据权限
- 在后端对应的controller中的list方法中加上
@PermissionData
注解,该注解有一个pageComponent参数为数据隔离的菜单的前端组件路径,如果配置了只有该前端组件路径会被拦截,不配置的话就拦截全部请求。
大家可能注意到了,官方文档中给出了系统上下文变量有sys_user_code
, 有sys_user_name
,有sys_org_code
但就是没有SQL表达式中的#{sys_user_id}
,而我们想要实现的效果只能给予user_id来实现,那么我们要怎么实现呢?
修改底层源码
笔者先给出如何修改,实现将当前登录用户的id存入数据权限的系统上下文变量中,来探讨其源码是如何进行修改的。
以下文件直接通过idea的search every where 的功能即可找到对应类
3.1. SysUserCacheInfo中添加一个String类型的sysUserId和其对应的setter/getter
1 | java复制代码... |
3.2. SysBaseApiImpl中的getCachUser为3.1的CacheInfo的新增的属性赋值
1 | java复制代码public SysUserCacheInfo getCacheUser(String username) { |
3.3. DataBaseConstant中添加数据权限的系统上下文的key值
1 | java复制代码... |
3.4. 在JwtUtil中的getUserSystemData放将sys_user对应的value设置为当前用户的id
1 | java复制代码... |
基于上面的4个步骤的小改动,我们就能够成功的为jeecg的数据权限添加上当前用户id值了。然后进行测试应该就能发现我们成功进行了基于user_id的多表关联的数据隔离啦。
底层深探
通过对@PermissionData
注解的跳转,我们能看到其对应着一个名为PermissionDataAspect
的切面
在切面中查询出了对应用户的数据权限模型(PermissionDataRuleModel),并且于当前用户的userInfo都放入了request请求中。
数据权限模型的值如下,可以看到,其值就是我们刚刚在后台中配置的数据规则
而JeecgDataAuthorUtils.installUserInfo的代码如下:
其将userinfo存放在了request当中(setter),但该切面到此处就结束了。 我们再看看整个进入Controller的list方法:
1 | java复制代码/** |
有一个QueryGenerator.initQueryWrapper
其传入了Request的ParameterMap,这个parameterMap中包含了在切面是存入的userInfo和datarulemodel,所以数据的隔离会不会是在这里实现的呢? 我们在进去看看,
再进入这里installMplus方法:
看到这,我们应该就找到了jeecg中居于mybatis plus实现的数据权限/隔离的代码所在地啦,但这个类十分的冗余判断,难以阅读,所以作者给出几个该类中和数据权限有关的代码:
installMplus方法中通过我们之前在切面中传入的SysPermissionDataRuleModel
开始解析我们自定义的SQL语句中的模板变量。
这个getSqlRuleValue方法就是模板替换的核心啦
1 | java复制代码public static String getSqlRuleValue(String sqlRule){ |
getSqlRuleValue方法的debug变量如下
JwtUtil中的getSystemData的代码如下:其实就是一大堆的if-else + contstant常量罢了
1 | java复制代码/** |
总结
数据隔离实际上就是对SQL进行拼接,如果只是对每个权限挨个写一个SQL的话,谁都会。其难点其实主要在于如何在不污染业务代码的情况下完成SQL的拼接,Jeecg通过切面 + MyBatisPlus的QueryWrapper进行实现了这样的效果。
本文转载自: 掘金