百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

快速开发平台 ruoyi-vue-pro(6)数据权限功能设计分析和实测

toyiye 2024-06-21 12:28 26 浏览 0 评论

我们在之前已经分析过该项目的搭建和一部分功能实测体验

该项目历史文章:

快速开发平台 ruoyi-vue-pro(1)- 项目搭建和功能体验

快速开发平台 ruoyi-vue-pro(2)工作流引擎模块

快速开发平台 ruoyi-vue-pro (3) - 账号授权体系实测分析

快速开发平台 ruoyi-vue-pro(4)- 商城移动端功能实测

快速开发平台 ruoyi-vue-pro(5)支付中心功能设计分析和实测


今天我们来看一下项目中的数据权限功能是怎么用的,以及它的技术设计思路

  • 数据权限功能测试

首先我们可以在角色管理中针对角色进行数据权限的维护,可以看到,数据权限是基于 角色+部门 来实现的,权限范围也是围绕着用户自身和所在的部门来设计的,我接下来定义两个角色来测试一下。



增加了两个角色,一个普通的,只能看自己的数据;另一个是小组长,可以看本部门以及下级部门的数据

我们把两个角色都开通“用户管理”这个功能菜单,现在我们试试用test1普通员工的权限登录一下

可以看到在这个菜单里,test1普通员工的账号因为数据权限是仅限自己的数据,所以只能看到自己。

然后使用test2账号进行登录,发现如数据权限范围所设定的一样,他可以看到研发部门下的所有数据。

其余的数据权限范围我就不逐一去测试了,其实最主要用的就是这种场景。

  • 技术原理分析

首先我们找到数据权限模块的底层工程,进入它的配置类从而开始分析

其主要定义了3个Bean。

  1. DataPermissionRuleFactory
    数据权限规则工厂,用来将项目中的数据权限规则的操作做成对外服务的统一出口,可以看到这个工厂接口定义了2个主要方法,即“获得所有数据权限规则数组”,“获得指定 Mapper 的数据权限规则数组”。这里的Mapper其实就是mybatis的dao接口,数据权限最终的实现是要基于数据库查询的操作类,给它的sql注入数据权限过滤的片段。

目前项目中只实现了一个默认的DataPermissionRuleFactoryImpl,其实可以理解成就是这个工厂就是一个util类而已,它这里的工厂模式设计是便于后面的扩展,比如具体的Rule配置可以直接从数据库获取,这样只需要新写一个FactoryImpl即可。

  1. DataPermissionDatabaseInterceptor

这个是数据权限拦截器,可以从上面的图中看到,具体这个Bean内部其实就是增加了一个Mybatis的拦截器

这个拦截器实现了Mybatis plus的 sql解析支持类和拦截接口,另外可以看到,它需要将上面定义的DataPermissionRuleFactory注入进来,其实也就是DataPermissionRuleFactoryImpl

  1. DataPermissionAnnotationAdvisor

基于spring aop的拦截切面设置,可以看到它这里主要是拦截 @DataPermission 的类和方法,最后合并起来形成一个切面


上面分析了数据权限模块的第一个配置类,它其实还有第二个

这个是基于部门的数据权限相关的配置,它主要是将项目工程里所有的DeptDataPermissionRuleCustomizer实现类抓取到,然后将权限API操作类注入到一个实例化的rule,最后将rule进行自定义操作方法调用后返回。

说简单点,就是把rule对象丢到项目里每个自定义的DeptDataPermissionRuleCustomizer里去customize

搜索一下,项目里的system工程,就自定义了一个,放在数据权限实现里去理解,它这里就是配置了 system_users 表需要按照dept_id去实现部门权限控制;而system_dept 表给定了字段,那就直接按照id来控制

可以看出来,整个数据权限控制细化到表和字段级别的配置,是直接在代码里控制的!而界面上能操作的只是给角色定义好 部门权限范围


配置的部分看完了,接下来看看核心拦截过程!

数据权限拦截的核心主要集中在DataPermissionDatabaseInterceptor

可以大致分为 before builder process 三个部分,其中before就是在查询或者预处理阶段针对规则引擎进行初始化等操作;而builder是将配置好的具体数据权限规则转化为具体按表按字段的sql片段,process就是具体的执行过程,其内部也是调用到builder部分。

整体可以理解为是一个sql拆解重新拼装的过程,首先利用mybatis的sql解析插件进行sql拆解,然后在条件拼装过程中加载到数据权限的配置,然后进行sql条件重写。我们可以重点看下这个方法

这里先是从上下文中拿到配置的rules(在之前的before 里初始化的),然后从table对应的Rule里去获取表达式getExpression,而具体的Expression表达式是我们之前看到的Rule的实现类DeptDataPermissionRule里定义的

public Expression getExpression(String tableName, Alias tableAlias) {
        // 只有有登陆用户的情况下,才进行数据权限的处理
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        if (loginUser == null) {
            return null;
        }
        // 只有管理员类型的用户,才进行数据权限的处理
        if (ObjectUtil.notEqual(loginUser.getUserType(), UserTypeEnum.ADMIN.getValue())) {
            return null;
        }

        // 获得数据权限
        DeptDataPermissionRespDTO deptDataPermission = loginUser.getContext(CONTEXT_KEY, DeptDataPermissionRespDTO.class);
        // 从上下文中拿不到,则调用逻辑进行获取
        if (deptDataPermission == null) {
            deptDataPermission = permissionApi.getDeptDataPermission(loginUser.getId());
            if (deptDataPermission == null) {
                log.error("[getExpression][LoginUser({}) 获取数据权限为 null]", JsonUtils.toJsonString(loginUser));
                throw new NullPointerException(String.format("LoginUser(%d) Table(%s/%s) 未返回数据权限",
                        loginUser.getId(), tableName, tableAlias.getName()));
            }
            // 添加到上下文中,避免重复计算
            loginUser.setContext(CONTEXT_KEY, deptDataPermission);
        }

        // 情况一,如果是 ALL 可查看全部,则无需拼接条件
        if (deptDataPermission.getAll()) {
            return null;
        }

        // 情况二,即不能查看部门,又不能查看自己,则说明 100% 无权限
        if (CollUtil.isEmpty(deptDataPermission.getDeptIds())
            && Boolean.FALSE.equals(deptDataPermission.getSelf())) {
            return new EqualsTo(null, null); // WHERE null = null,可以保证返回的数据为空
        }

        // 情况三,拼接 Dept 和 User 的条件,最后组合
        Expression deptExpression = buildDeptExpression(tableName,tableAlias, deptDataPermission.getDeptIds());
        Expression userExpression = buildUserExpression(tableName, tableAlias, deptDataPermission.getSelf(), loginUser.getId());
        if (deptExpression == null && userExpression == null) {
            // TODO 芋艿:获得不到条件的时候,暂时不抛出异常,而是不返回数据
            log.warn("[getExpression][LoginUser({}) Table({}/{}) DeptDataPermission({}) 构建的条件为空]",
                    JsonUtils.toJsonString(loginUser), tableName, tableAlias, JsonUtils.toJsonString(deptDataPermission));
//            throw new NullPointerException(String.format("LoginUser(%d) Table(%s/%s) 构建的条件为空",
//                    loginUser.getId(), tableName, tableAlias.getName()));
            return EXPRESSION_NULL;
        }
        if (deptExpression == null) {
            return userExpression;
        }
        if (userExpression == null) {
            return deptExpression;
        }
        // 目前,如果有指定部门 + 可查看自己,采用 OR 条件。即,WHERE (dept_id IN ? OR user_id = ?)
        return new Parenthesis(new OrExpression(deptExpression, userExpression));
    }

可以看出来,在获取表达式过程中,规则里加载了用户登录信息、权限配置,最终形成了一个实际的sql过滤条件。

最后的最后,就是把过滤条件组装好后重写SQL,切到Mybatis执行SQL的前夕,让它执行重写后的叠加了数据权限的SQL。


至于SQL解析的细节,我们就不赘述了,可以去参考下sql解析插件的机制。


  • 总结

我们本篇小小测试了一下项目中的数据权限功能。

1、系统支持在角色中配置 用户&部门级别的数据权限范围,然后系统的控制器中,追加 @DataPermission 注解(其实不加也可以,默认就是打开的,它这里是反选逻辑,如果加了并且参数里进行了禁用,则就表示不拦截),就能实现数据的拦截权限

2、设计上还不太完善的点:

如果需要去细化控制相同角色在不同功能上的数据权限是做不到的

数据权限控制涉及的表以及字段需要在代码里去配置

3、总体来说技术设计上还是比较优雅的,可扩展性也比较强

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码