六、数据权限使用介绍
数据权限配置有两种方式:
1、通过系统配置界面,实时配置生效。
2、通过代码注解配置。
适应的条件:
1、仅支持自定义 Mapper 的 SQL,不支持 Mybatis-Plus 抽象的 CRUD 方法。如果一个 CRUD 要实现数据权限,必须使用自定义 SQL 的方式,如果要支持 Mybatis-Plus 抽象的 CRUD 那么需要自定义插件,方法参考本文最后描述。
2、自定义 SQL 时,主表一定需要加别名。加别名的原因很简单,如果不加别名,那么代码里需要去解析 SQL 并判断表、字段、where、order by 等,在上面自动加别名,这样导致代码逻辑复杂从而影响性能,我们更倾向于遵循 SpringBoot 的设计原则:约定大于配置。
3、本数据权限插件可支持大多数的业务需求,重点就是:需要自定义 SQL。
4、数据权限主表,即数据表和数据权限表的关联关系表中,一定要有 organization_id 字段。
一、通过系统配置界面配置数据权限
系统配置的数据权限是通过系统配置界面将配置信息保存在数据库,然后系统启动时,将配置信息保存到 Redis 缓存来实现的。
1、系统管理 > 权限管理 > 数据权限,一定要选择具体需要做数据权限的接口,然后点击新建。数据权限是控制获取数据的接口请求,不是系统菜单。

2、新增数据权限规则,以及配置参数说明

参数说明:
- 数据权限名称 :自定义一个名称,方便查找和区分
- Mapper 全路径 :Mapper 路径配置到具体方法名称,例:com.gitegg.service.system.mapper.UserMapper.selectUserList
- 数据权限类型 :数据权限控制的不同范围,这里会和用户的角色配置相关联。实际是一个角色的数据权限范围,角色数据权限的可视范围。
只能查看本人(实现原理是在查询条件添加数据表的 creator 条件)
只能查看本部门 (实现原理是在查询条件添加数据表的部门条件)
只能查看本部门及子部门 (实现原理是在查询条件添加数据表的部门条件)
可以查看所有数据(不处理)
自定义(添加 where 子条件) - 权限主表 :即数据表和数据权限表的关联关系表,需要过滤据权限的关联表,比如此例中是查询用户信息,那么权限主表就是用户表和组织机构数据权限有关联的表,这里必须是自定义 SQL 语句中已有的表,主表或者 JOIN 表都可以。
- 权限主表别名 :数据权限本质是通过自定义插件,实现 SQL 语句的拼接,从而过滤用户拥有的数据权限数据,所以在拼接 SQL 语句时,多表 JOIN 需要给表设置别名,那么这个字段就是设置权限主表的别名。
- 数据权限表 :用于数据权限的配置表,本例中是指 t_sys_data_permission_user 表(这张表里存的是组织机构和用户的关联关系,在新建用户时,会选择用户的机构,那么默认该用户是拥有该机构的权限的,如果用户拥有多个机构的数据权限,那么在新建用户之后,用户列表后面的操作按钮中选择用户的多个机构数据数据权限。),如果数据权限表没有跟需要查询的数据权限有直接的关联关系,那么需要有其他中间表来关联,那么这个中间表就是需要配置的数据权限的主表。
- 数据权限表别名 :拼接 SQL 语句时的表别名。
- 需排除的字段 :列级数据权限,配置没有权限查看的字段,表示该数据权限配置查询的结果中需要去除的字段。
- 仅保留的字段 :列级数据权限,配置有权限查看的字段,表示该数据权限配置查询的结果仅保留的字段。
- 自定义表达式 :有时我们可能会对数据权限进行额外的限制,此字段是拼接在 where 条件后面的自定义语句(做好系统安全,如果这里随便配置,那么会有 SQL 注入的风险)。
- 状态 :数据权限是否启用的标识。
- 备注 :对此数据权限的备注信息。
3、配置适应执行数据权限的角色(和数据权限类型字段相匹配)
通常,经典的 RBAC 模型中,我们角色是和资源权限相关联的,而在此处,我们将角色与数据权限类型相关联,就是在角色设计时,要考虑的就是这个角色可以查看的数据权限范围。通过配置可知,数据权限的控制是从两方面进行的,一方面是用户和组织机构的数据权限关联,一方面是角色和数据权限的类型关联的。
数据权限插件控制数据权限的流程:数据权限插件在组装 SQL 时,首先通过前缀匹配查询 mapper 的 statementId 是否在缓存中,如果存在,那么取出当前用户角色相匹配的数据权限类型,组装好带有数据权限类型的 DataPermission 缓存 Key,从缓存中取出数据权限配置。
二、通过注解配置数据权限
**系统中,通过注解的方式进行数据权限默认是关闭的,需要在配置文件中设置 data-permission.annotation-enable: true 开启注解的数据权限。**<br /> 通过注解的方式来实现数据权限配置,在开发人员进行开发的时候,可能显得方便一些,哪些接口需要进行数据权限,那么直接注解使用就可以了。但是,此种方法并不灵活,如果数据权限有变化,那么还需要修改代码并重新发布到线上环境。
1、在**Mapper.java 中的接口增加@DataPermission 注解
/**
* 查询用户列表
* @param page
* @param user
* @return
*/
@DataPermission(dataTableName = "t_sys_organization_user", dataTableAlias = "organizationUser", dataPermissionType = "3", innerTableName = "t_sys_organization", innerTableAlias = "orgDataPermission")
@DataPermission(dataTableName = "t_sys_organization_user", dataTableAlias = "organizationUser", dataPermissionType = "2", innerTableName = "t_sys_organization", innerTableAlias = "orgDataPermission")
@DataPermission(dataTableName = "t_sys_organization_user", dataTableAlias = "organizationUser", dataPermissionType = "1", innerTableName = "t_sys_organization", innerTableAlias = "orgDataPermission")
Page<UserInfo> selectUserList(Page<UserInfo> page, @Param("user") QueryUserDTO user);
2、@DataPermission 注解参数说明
@DataPermission 注解中的配置参数,可以参考配置界面中的参数说明,实现的是相同功能,只是两者的实现方式不一样:配置界面是可以实时配置实时生效的,注解的方式是在代码中,每次代码运行时,会根据注解配置进行数据权限控制。
/**
* 需要做数据权限主表
*/
String dataTableName() default "";
/**
* 需要做数据权限表的别名
*/
String dataTableAlias() default "";
/**
* 数据权限需要排除的字段
*/
String dataColumnExclude() default "";
/**
* 数据权限需要保留的字段
*/
String dataColumnInclude() default "";
/**
* 数据权限表
*/
String innerTableName() default "t_sys_organization";
/**
* 数据权限表的别名
*/
String innerTableAlias() default "dpOrg";
/**
* 数据权限类型:1只能查看本人 2只能查看本部门 3只能查看本部门及子部门 4可以查看所有数据 5 自定义
*/
String dataPermissionType() default "";
/**
* 自定义数据权限类型
*/
String customExpression() default "";
三、插件扩展支持 Mybatis-Plus 的 CRUD 抽象接口
Mybatis-Plus 把 CRUD 抽象成了统一的 Mapper,所有不需要写 SQL 的 CRUD 都用的 BaseMapper。我们所有的业务 Mapper 都继承了这个 BaseMapper。在实际调用 CRUD 数据库操作的时候,通过 mybatis 自定义插件的 MappedStatement 可以获取到使用的业务子 Mapper。所以,这种情况可以在业务子 Mapper 添加注解,配置此 Mapper 是否需要单表数据权限、主表别名等信息。自定义插件拦截这些要执行的 Mapper 进行判断和修改。