数据权限的设计与实现系列9——前端筛选器组件Everright-filter集成框架开发2

news2024/12/27 16:04:30

功能实现

规则转换为 SQL 片段‍

规则解析

首先我们来构造一个典型的规则,包括两个条件组,每个组由两个条件组成,由且与或两种逻辑关系,如下图:

然后看看生成的规则,如下:

{
    "filters": [
        {
            "conditions": [
                {
                    "property": "paramName",
                    "value": "1"
                },
                {
                    "operator": "contains",
                    "property": "paramKey",
                    "value": "password"
                }
            ],
            "logicalOperator": "and"
        },
        {
            "conditions": [
                {
                    "property": "paramValue",
                    "value": "1"
                },
                {
                    "operator": "empty",
                    "property": "orderNo"
                }
            ],
            "logicalOperator": "or"
        }
    ],
    "logicalOperator": "and"
}

最后,分析其数据结构,可以分成三层。

  1. 最外层是一个对象,由两个属性组成,一是 filters,条件组集合;二是 logicalOperator,逻辑关系(and或者or)。
  2. filter 对象自身,同样由两个属性组成,一是 conditions,条件集合;;二是 logicalOperator,逻辑关系。
  3. condition 对象,由三个属性组成,一是 property,属性;二是 operator,操作符;三是 value,值。若为相等操作,则操作符属性可省略。


同时,对于数值类,日期类,可能进行区间查询,筛选器生成的规则中,condition 对象仍有三个属性组成不变,只是 value 值变成了一个两元素的数组,如:

{"operator":"between","property":"age","value":[18,60]}

对象构建

我们在视图对象层 vo 中创建规则对应的数据对象,遵循由内到外的方式来构建。
首先是最内层的筛选条件,属性和操作符是固化的,值可能是单元素,也可能为数组,类型可能是字符串,也可能是数值。对于前端 js,属于非强类型语言,可以灵活设置,但对于后端强类型的 java,需要将其设置为 Object 类型,进行二次解析。

package tech.abc.platform.entityconfig.vo;

import lombok.Data;

/**
 * 数据筛选条件
 *
 * @author wqliu
 * @date 2024-08-06
 */
@Data
public class DataFilterConditionVO {

    /**
     * 属性
     */
    private String property;

    /**
     * 操作
     */
    private String operator;

    /**
     * 值
     */
    private Object value;

}

然后是中间层,数据筛选组,如下:

package tech.abc.platform.entityconfig.vo;

import lombok.Data;

import java.util.List;

/**
 * 数据筛选组
 *
 * @author wqliu
 * @date 2024-08-06
 */
@Data
public class DataFilterGroupVO {

    /**
     * 数据筛选条件集合
     */
    private List<DataFilterConditionVO> conditions;

    /**
     * 逻辑操作
     */
    private String logicalOperator;
}

最后是最外层的规则,如下:

package tech.abc.platform.entityconfig.vo;

import lombok.Data;

import java.util.List;

/**
 * 数据筛选规则
 *
 * @author wqliu
 * @date 2024-08-06
 */
@Data
public class DataFilterRuleVO {

    /**
     * 数据筛选组集合
     */
    private List<DataFilterGroupVO> filters;

    /**
     * 逻辑操作
     */
    private String logicalOperator;
}

数据传递

在规则配置页面,再加一个按钮,将规则传给后端,如下:

前端代码如下:

    // 生成SQL片段
    generateSqlPart() {
      // 先调用一次生成规则,避免配置调整了规则未同步
      this.generateRule()
      // 调用后端服务转换
      this.api.generateSqlPart(this.entityData.modelId, this.entityData.rule).then((res) => {
        this.entityData.sqlPart = res.data
      })
    }
// 实体模型数据权限
export const entityModelDataPermission = Object.assign({}, COMMON_METHOD, {
  serveUrl: '/' + moduleName + '/' + 'entityModelDataPermission' + '/',
  // 获取实体模型完整属性列表
  getOrInit(modelId) {
    return request.get({ url: this.serveUrl + 'getOrInit', params: { modelId } })
  },
  // 生成sql片段
  generateSqlPart(entityModelId, rule) {
    return request.post({ url: this.serveUrl + entityModelId + '/generateSqlPart', data: rule })
  }
})

后端控制器如下:

    /**
     * 生成SQL片段
     */
    @PostMapping("/{id}/generateSqlPart")
    @SystemLog(value = "实体模型数据权限-生成SQL片段")
    @PreAuthorize("hasPermission(null,'entityconfig:entityModelDataPermission:generateSqlPart')")
    public ResponseEntity<Result> generateSqlPart(@PathVariable("id") String id, @RequestBody String rule) {
        String sqlPart = entityModelDataPermissionService.generateSqlPart(id, rule);
        return ResultUtil.success(sqlPart);
    }

我们尝试使用 FastJson,将字符串解析成对象,后端服务如下:

 @Override
    public String generateSqlPart(String id, String rule) {
        DataFilterRuleVO dataFilterRule = JSON.parseObject(rule, DataFilterRuleVO.class);
        return "";

    }

进入调试模式,查看数据,已完成解析工作,如下:

转换处理

接下来,我们开始最复杂的转换处理,同样遵循由简单到复杂的情况。
首先,最简单的,只有一个逻辑组,组中只有一个条件,如下:

转换方法如下:

   @Override
    public String generateSqlPart(String id, String rule) {
        String result = "";
        // 转换数据
        DataFilterRuleVO dataFilterRule = JSON.parseObject(rule, DataFilterRuleVO.class);
        // 获取组集合
        List<DataFilterGroupVO> dataFilterGroupList = dataFilterRule.getFilters();
        // 遍历组集合
        for (DataFilterGroupVO dataFilterGroup : dataFilterGroupList) {
            // 获取条件集合
            List<DataFilterConditionVO> conditionList = dataFilterGroup.getConditions();
            // 遍历条件集合
            for (DataFilterConditionVO condition : conditionList) {
                // 获取字段名,命名风格驼峰转换成下划线
                String fieldName = CommonUtil.camelToUnderline(condition.getProperty());
                // 获取操作
                String operator = condition.getOperator();
                if (StringUtils.isEmpty(operator)) {
                    operator = "=";
                }
                Object value = condition.getValue();

                result = fieldName + " " + operator + " '" + value + "'";
            }

        }

        return result;

    }

转换结果:

可以看到,输出了预期的 sql 片段。‍

启用数据权限组件

调整 Mybatisplus 的插件配置,增加数据权限插件,如下图所示:

注意,数据权限插件是后期新增的功能,要求 mybatisplus 的版本不能太低,建议 mybatis-plus-boot-starter 的版本使用 3.5.4。

其中数据权限处理器,需要自行实现,如下:

package tech.abc.platform.framework.extension;

import com.baomidou.mybatisplus.extension.plugins.handler.MultiDataPermissionHandler;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Table;
import org.apache.commons.lang3.StringUtils;
import tech.abc.platform.common.exception.SessionExpiredException;
import tech.abc.platform.common.utils.SpringUtil;
import tech.abc.platform.common.utils.UserUtil;
import tech.abc.platform.entityconfig.service.EntityModelDataPermissionService;

/**
 * 数据权限处理器
 *
 * @author wqliu
 * @date 2024-08-03
 */
@Slf4j
public class MyDataPermissionHandler implements MultiDataPermissionHandler {


    /**
     * 数据权限配置表名称
     */
    public static final String DATA_PERMISSION_CONFIG_TABLE_NAME = "cfg_entity_model_data_permission";

    @Override
    public Expression getSqlSegment(Table table, Expression where, String mappedStatementId) {


        // 数据权限相关的 SQL 片段
        String tableName = table.getName();
        // 排除数据权限配置表,否则会导致死循环
        if (tableName.equals(DATA_PERMISSION_CONFIG_TABLE_NAME)) {
            return null;
        }

        // 获取数据权限 SQL 片段
        EntityModelDataPermissionService entityModelDataPermissionService = SpringUtil.getBean(EntityModelDataPermissionService.class);
        String sqlSegment = entityModelDataPermissionService.getDataPermissionSqlPart(tableName);


        if (sqlSegment != null) {

            String userId = "";
            // 登录阶段,获取不到当前用户信息,视为不做数据权限过滤
            try {
                userId = UserUtil.getId();
            } catch (SessionExpiredException e) {
                return null;
            }

            sqlSegment = StringUtils.replace(sqlSegment, "{@CurrentUserId@}", userId);

            try {
                return CCJSqlParserUtil.parseCondExpression(sqlSegment);
            } catch (JSQLParserException e) {
                log.error("数据权限 SQL 片段解析失败", e);
                return null;
            }
        } else {
            return null;
        }
    }
}

这里有几个需要特别注意的点:

  1. 需要将数据权限配置表排除,否则会导致死循环引发堆栈溢出。
  2. 通过平台的工具类 UserUtil 来获取到当前用户信息,但在系统登录环节,尚未完成认证,会抛出会话超时异常,同样需要忽略,否则会影响正常系统登录。
  3. 在本环节中,可以将平台约定预置的运行期变量,如当前用户标识、当前用户所在部门等,替换为真实的运行数据,如 sqlSegment = StringUtils.replace(sqlSegment, “{@CurrentUserId@}”, userId);

查看效果

访问平台的系统参数菜单,执行查询操作,如下图:

可以看到,数据权限过滤已经发挥了作用,控制台打印输出的 sql,无论是获取分页的 sql,还是最终执行的 sql,都自动追加了数据权限过滤的 SQL 片段,如下:

==>  Preparing: SELECT COUNT(*) AS total FROM sys_param WHERE delete_flag = 'NO' AND param_value = '10'
==> Parameters: 
<==    Columns: total
<==        Row: 2
<==      Total: 1
==>  Preparing: SELECT id, param_name, param_key, param_value, order_no, delete_flag, create_id, create_time, update_id, update_time, version FROM sys_param WHERE delete_flag = 'NO' AND param_value = '10' ORDER BY order_no ASC LIMIT ?
==> Parameters: 10(Long)
<==    Columns: id, param_name, param_key, param_value, order_no, delete_flag, create_id, create_time, update_id, update_time, version
<==        Row: 1158917826028863489, 用户登录最多输错次数, PASSWORD_INPUT_ERROR_TIMES, 10, 004, NO, 1, 2019-08-07 09:48:36, 1, 2020-08-26 03:21:29, 5
<==        Row: 1158917976734400513, 账号锁定自动解锁时间间隔(分), ACCOUNT_UNLOCK_INTERVAL, 10, 005, NO, 1, 2019-08-07 09:49:12, , 2023-03-24 10:47:21, 3
<==      Total: 2

小结

至此,已完成了数据权限的整体框架开发,但也只是框架,各环节还有大量的工作需要完善,主要包括以下几点:

  1. 配置规则时需要增加用户、部门、角色等维度
  2. 将数据筛选器生成的各种场景下的复杂规则转换为 SQL 片段
  3. 读取平台运行时变量,替换掉预置的用户、部门等占位符

开源平台资料

平台名称:一二三开发平台
简介: 企业级通用开发平台
设计资料:[csdn专栏]
开源地址:[Gitee]
开源协议:MIT
如果您在阅读本文时获得了帮助或受到了启发,希望您能够喜欢并收藏这篇文章,为它点赞~
请在评论区与我分享您的想法和心得,一起交流学习,不断进步,遇见更加优秀的自己!

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

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

相关文章

spring中对于servlet API的封装---springWeb

目录 一.springweb概述 二.springweb的特点 三.springweb的运行流程 四.springweb组件 五.springweb的搭建 1.导包 2.配置 DispatcherServlet 3.开启 springweb 注解 4.处理器的搭建 六.springweb注解 七.springweb拦截器 1.拦截器概述 2.拦截器的实现 (1)添加 servelt api 依赖…

开源 AI 智能名片链动 2+1 模式 O2O 商城小程序在社群活动中的应用与时机选择

摘要&#xff1a;本文探讨了开源 AI 智能名片链动 21 模式 O2O 商城小程序在社群经济中的重要性&#xff0c;着重分析了如何借助该小程序适时举办大型活动以维持和引爆社群活跃度。通过对活动时机选择的研究&#xff0c;强调了针对社群用户量身定制活动时机的必要性&#xff0c…

基于python+django+vue的外卖管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于pythondjangovueMySQL的外…

C/C++笔记

C/CPP笔记 杂记 struct msg_train和typedef struct msg_train 大小不一样 cstdio和stdio #include <stdio.h>int main() {printf("Hello, World!\n");return 0; } #include <cstdio>int main() {std::printf("Hello, World!\n");return 0; } 命…

20个Python入门基础语法要点

今天&#xff0c;我们将聚焦于Python的20个基础语法要点&#xff0c;让你的编程之旅更加顺畅。 第一部分&#xff1a;环境搭建与基本概念 1. Hello, World! 你的第一行代码&#xff1a;这是编程旅程的传统起点。 这行代码告诉Python显示文本&#xff0c;print是关键函数&…

常耀斌:AI赋能企业数字化转型(清华社发行)

新书地址&#xff1a; 清华出版社&#xff1a;清华大学出版社-图书详情-《AI赋能企业数字化转型》 京东&#xff1a;《AI赋能企业数字化转型 常耀斌 清华大学出版社 9787302669081》【摘要 书评 试读】- 京东图书 内容简介&#xff1a; 在数字经济时代&#xff0c;企业发…

曲线图如何绘制美观,曲线图10种美化方法

曲线图是比较常用的图形&#xff0c;本文以二维曲线图为例&#xff0c;展示曲线的图的不同美化方法&#xff0c;如图1所示&#xff0c;是一个标准的曲线图&#xff0c;横坐标为x&#xff0c;纵坐标为y, 图1 标准曲线图 调整方法1 首先可以通过改变线的颜色&#xff0c;不同…

从零开始学PostgreSQL (十四):高级功能

目录 1. 简介 2. 视图 3. 外键 4. 事务 5. 窗口函数 6. 继承 7. 结论 简介 PostgreSQL是一个强大且开源的关系型数据库管理系统&#xff0c;以其稳定性、功能丰富性和对SQL标准的广泛支持而闻名。它不仅提供了传统的关系型数据库功能&#xff0c;如事务处理、外键约束和视图&am…

递归基础训练-路径总和

路径总和 给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径&#xff0c;这条路径上所有节点值相加等于目标和 targetSum 。如果存在&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 我们可以把之前的…

【图虫创意-注册安全分析报告-无验证方式导致安全隐患】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 1. 暴力破解密码&#xff0c;造成用户信息泄露 2. 短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉 3. 带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造…

R语言统计分析——散点图2(散点图矩阵、高密度散点图)

参考资料&#xff1a;R语言实战【第2版】 1、散点图矩阵 pairs()函数可以创建基础的散点图矩阵。下面代码用于绘制一个散点图矩阵&#xff0c;包含mtcars数据集中的mpg、disp、drat和wt四个变量&#xff1a; pairs(~mpgdispdratwt,datamtcars,main"Basic Scatter Plot M…

输煤传送带异物识别检测数据集 yolo数据集 2400张

输煤传送带异物识别检测数据集 yolo数据集 2400张 输煤传送带异物识别检测数据集介绍 数据集名称 输煤传送带异物识别检测数据集&#xff08;Conveyor Belt Foreign Object Detection Dataset&#xff09; 数据集概述 该数据集专为输煤传送带上的异物识别检测设计&#xff0…

Unity携程Coroutine用法

一.携程概述 官方的解释是&#xff0c;携程允许你可以在多个帧中执行任务。在Unity中&#xff0c;携程是一个可以暂停并在后续帧中从暂停处继续执行的方法。 二.携程写法 下面示例使用携程和Update打印前5帧的时间间隔&#xff0c;展示了携程的基础写法 using System.Colle…

vmware + ubuntu + 初始配置(超级用户权限、vim安装、ssh登陆、共享文件夹、git)

1 VMware Ubuntu下载与安装 下载与安装 2 使用超级用户权限 &#xff08;1&#xff09;执行命令&#xff1a;sudo passwd root 然后在弹出的密码中输入密码即可&#xff0c;具体如下&#xff1a; 第一个密码是当前用户密码 后面两个是root用户密码 //推荐使用一个密码 3 vi…

SEMIDRIVE X9E Flash 调试要点

一、前言 客户采用芯驰 X9E 平台做的 T-BOX 产品&#xff0c;因为客户选用的 Flash 型号不在 SemiDrive_Memory 支持列表里面&#xff0c;出现机器能烧录不能启动的问题。接下来我们对这个问题进行调试。 二、SEMIDRIVE X9E Flash 调试要点 ① 客户的板子 Flash 型号为 GD25LQ…

43集 ESP32 编译调试出错的解决方法汇总

43集 ESP32 编译调试出错的解决方法汇总 1、提示找不到如下头文件&#xff0c;分别对应adf的component #include “esp_peripherals.h” esp_peripherals #include “audio_element.h” audio_pipeline #include “audio_common.h” audio_pipeline 这几个头文件都是esp-adf里…

【全网首发】2024华为OD机试 E卷D卷抽中题库清单(全真题库,持续更新)含考点说明

华为OD机试 2024E卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;E卷D卷A卷B卷C卷&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;私信哪吒&#xff0c;备注华为OD&#xff0c;加…

Git 原理(提交对象)(结合图与案例)

Git 原理&#xff08;提交对象&#xff09; 这一块主要讲述下 Git 的原理。 在进行提交操作时&#xff0c;Git 会保存一个提交对象&#xff08;commit object&#xff09;&#xff1a; 该提交对象会包含一个指向暂存内容快照的指针&#xff1b; 该提交对象还包含了作者的姓…

【MYSQL中数据库的约束以及表的设计】

MYSQL中数据库的约束和表的设计 一、数据库的约束1.1 NULL约束1.2 UNIQUE&#xff1a;唯一约束1.3 DEFAULT&#xff1a;默认值约束1.4 PRIMARY KEY&#xff1a;主键约束1.5 FOREIGN KEY &#xff1a;外键约束1.6 CHECK 约束 二、表的设计2.1 第一范式&#xff08;1NF&#xff0…

Python面试宝典第49题:字符串压缩

题目 给你一个字符数组chars &#xff0c;请使用下述算法进行压缩。 1、从一个空字符串s开始&#xff0c;对于chars中的每组连续重复字符 &#xff1a; &#xff08;1&#xff09;如果这一组长度为1 &#xff0c;则将字符追加到s中。 &#xff08;2&#xff09;否则&#xff0c…