当前位置: 首页 > news >正文

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

功能实现

规则转换为 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,将字符串解析成对象,后端服务如下:

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

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

转换处理

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

转换方法如下:

   @Overridepublic 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";@Overridepublic 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
如果您在阅读本文时获得了帮助或受到了启发,希望您能够喜欢并收藏这篇文章,为它点赞~
请在评论区与我分享您的想法和心得,一起交流学习,不断进步,遇见更加优秀的自己!

相关文章:

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

功能实现 ‍ 规则转换为 SQL 片段‍ 规则解析 首先我们来构造一个典型的规则&#xff0c;包括两个条件组&#xff0c;每个组由两个条件组成&#xff0c;由且与或两种逻辑关系&#xff0c;如下图&#xff1a; 然后看看生成的规则&#xff0c;如下&#xff1a; {"filt…...

鸿蒙Harmony-Next 徒手撸一个日历控件

本文将介绍如何使用鸿蒙Harmony-Next框架实现一个自定义的日历控件。我们将创建一个名为CalendarView的组件&#xff08;注意,这里不能叫 Calendar因为系统的日历叫这个&#xff09;,它具有以下功能: 显示当前月份的日历支持选择日期显示农历日期可以切换上一月和下一月 组件…...

直播音频解决方案

音频解决方案公司具体解决的是什么样的问题&#xff1f;什么样的客户需要找音频方案公司&#xff1f;相信还是有很多人不是很了解。音频解决方案公司工作就像是为音频设备“量身定制衣服”&#xff0c;帮助客户解决各种音频相关的问题。无论你是音响制造商、会议设备商、耳机品…...

Git基本用法总结

设置全局用户名 git config --global user.name xxx #设置全局用户名 设置全局邮箱地址 git config --global user.email xxxxxx.com #设置全局邮箱地址 查看所有的 Git 配置&#xff0c;包括用户信息 git config --list #查看所有的 Git 配置&#xff0c;包括用户信…...

SQLite的入门级项目学习记录(四)

性能评估和测试 规划项目 1、框架选择&#xff1a;前端交互和线程控制用pyside&#xff0c;SQLite作为数据库支持。 2、预估数据量&#xff1a;每秒10个数据&#xff0c;每个月约26000000&#xff08;26M&#xff09;条。 3、压力测试&#xff1a;首先用python脚本创建一个数据…...

Docker工作目录迁移

文章目录 前言一、迁移步骤1.停掉docker服务2.创建存储目录3.迁移docker数据4.备份5.添加软链接6.重启docker服务&#xff0c;测试 总结 前言 安装docker&#xff0c;默认的情况容器的默认存储路径会存储系统盘的 /var/lib/docker 目录下&#xff0c;系统盘一般默认 50G&#…...

【多维动态规划】64. 最小路径和(面试真题+面试官调整后的题目)

64. 最小路径和 难度&#xff1a;中等 力扣地址&#xff1a;https://leetcode.cn/problems/minimum-path-sum/description/ 1. 原题以及解法 1.1 题目 给定一个包含非负整数的 m x n 网格 grid &#xff0c;请找出一条从左上角到右下角的路径&#xff0c;使得路径上的数字总和…...

Web后端开发技术:RESTful 架构详解

RESTful 是一种基于 REST&#xff08;表述性状态转移&#xff0c;Representational State Transfer&#xff09;架构风格的 API 设计方式&#xff0c;通常用于构建分布式系统&#xff0c;特别是在 Web 应用开发中广泛应用。REST 是一种轻量级的架构模式&#xff0c;利用标准的 …...

【Fastapi】参数获取,json和query

【Fastapi】参数获取&#xff0c;json和query 前言giteegithub query形式json传递同步方法使用json 前言 花了半个月的时间看了一本小说&#xff0c;懈怠了…今天更新下fastapi框架的参数获取 gitee https://gitee.com/zz1521145346/fastapi_frame.git github https://git…...

【Node.js】初识微服务

概述 Node.js 的微服务架构是一种通过将应用程序分解为独立的、松耦合的小服务的方式进行系统设计。 每个微服务负责处理一个特定的业务功能&#xff0c;并且这些服务可以独立开发、部署、扩展和管理&#xff0c;并且可以通讯。 它的核心思想就是解耦。 微服务和微前端是类…...

React项目实战(React后台管理系统、TypeScript+React18)

### 项目地址:(线上发布) (1)别人的项目地址 gitgitee.com:zqingle/lege-react-management.git (2)我自己的项目地址 gitgitee.com:huihui-999/lege-react-management.git ### B站讲解视频地址 https://www.bilibili.com/video/BV1FV4y157Zx?p37&spm_id_frompageDrive…...

【专题】2024中国生物医药出海现状与趋势蓝皮书报告合集PDF分享(附原数据表)

原文链接&#xff1a;https://tecdat.cn/?p37719 出海已成为中国医药产业实现提速扩容的重要途径。目前&#xff0c;中国医药产业发展态势良好&#xff0c;创新能力不断增强&#xff0c;然而也面临着医保政策改革和带量集采带来的压力。政府积极出台多项政策支持医药企业出海…...

【iOS】KVC

文章目录 KVC的定义 容器类中KVC的实现 KVC设值 KVC取值 KVC使用KeyPath KVC处理异常 KVC处理设值nil异常 KVC处理UndefinedKey异常 KVC处理数值和结构体类型属性 KVC键值验证 KVC处理集合 简单集合运算符 对象运算符 KVC处理字典 KVC应用 动态地取值和设值 用…...

【2024年华为杯研究生数学建模竞赛C题】完整论文与代码

这里写目录标题 基于数据驱动下磁性元件的磁芯损耗建模一、问题重述1.1问题背景1.2问题回顾 问题分析与模型假设模型建立与求解 基于数据驱动下磁性元件的磁芯损耗建模 一、问题重述 1.1问题背景 在现代电力电子和变压器设计中&#xff0c;磁性元件是确保能量高效传递和系统稳…...

svn回退到以前历史版本修改并上传

svn回退到以前版本&#xff0c;并在以前版本上修改代码后&#xff0c;上传到svn库当中&#xff0c;如下步骤&#xff1a; 3、 以回退到版本号4为例&#xff1a;选中版本号4&#xff0c;右键->Revert to this version,在出现的对话框中 点击yes&#xff01; 4、 5、...

fiddler抓包07_抓IOS手机请求

课程大纲 前提&#xff1a;电脑和手机连接同一个局域网 &#xff08;土小帽电脑和手机都连了自己的无线网“tuxiaomao”。&#xff09; 原理如下&#xff1a; 电脑浏览器抓包时&#xff0c;直接就是本机网络。手机想被电脑Fiddler抓包&#xff0c;就要把Fiddler变成手机和网络…...

Windows系统及Ubuntu系统安装Java

Java语言简介 Java是一种高级编程语言&#xff0c;Java语言的创始可以追溯到1990年代初&#xff0c;当时任职于Sun Microsystems&#xff08;后来被甲骨文公司收购&#xff09;的詹姆斯高斯林&#xff08;James Gosling&#xff09;等人开始开发一种名为“Oak”(名字来源于詹姆…...

uni-data-select 使用 localdata 传入数据出现 不回显 | 下拉显示错误的 解决方法

目录 1. 问题所示2. 正确Demo3. 下拉显示错误(Bug复现)4. 下拉不回显(Bug复现)1. 问题所示 uni-app的下拉框uni-data-select 使用 localdata 传入数据 主要总结正确的Demo以及复现一些Bug 数据不回显数据不显示下拉选项2. 正确Demo 详细的基本知识推荐阅读:uni-app中的…...

图解 TCP 四次挥手|深度解析|为什么是四次|为什么要等2MSL

写在前面 今天我们来图解一下TCP的四次挥手、深度解析为什么是四次&#xff1f; 上一片文章我们已经介绍了TCP的三次握手 解析四次挥手 数据传输完毕之后&#xff0c;通信的双方都可释放连接。现在客户端A和服务端B都处于ESTABLISHED状态。 第一次挥手 客户端A的应用进…...

DevExpress中文教程:如何将WinForms数据网格连接到ASP. NET Core WebAPI服务?

日前DevExpress官方发布了DevExpress WinForms的后续版本——将.NET桌面客户端连接到安全后端Web API服务(EF Core with OData)&#xff0c;在本文中我们将进一步演示如何使用一个更简单的服务来设置DevExpress WinForms数据网格。 P.S&#xff1a;DevExpress WinForms拥有180…...

SpringBoot3核心特性-核心原理

目录 传送门前言一、事件和监听器1、生命周期监听2、事件触发时机 二、自动配置原理1、入门理解1.1、自动配置流程1.2、SPI机制1.3、功能开关 2、进阶理解2.1、 SpringBootApplication2.2、 完整启动加载流程 三、自定义starter1、业务代码2、基本抽取3、使用EnableXxx机制4、完…...

Linux:RPM软件包管理以及yum软件包仓库

挂载光驱设备 RPM软件包管理 RPM软件包简介 区分软件名和软件包名 软件名&#xff1a;firefox 软件包名&#xff1a;firefox-52.7.0-1.el7.centos.x86_64.rpm 查询软件信息 查询软件&#xff08;参数为软件名&#xff09; ]# rpm -qa #当前系统中所有已安装的软件包 ]# r…...

pod介绍与配置

1、pod概念介绍 Pod 是 kubernetes 基本调度单位。每个 Pod 中可以运 行一个或多个容器&#xff0c;共享 Pod 的文件系统、IP 和网络等资源&#xff0c;每个 Pod 只有一个 IP。 2、使用 yaml或json 文件创建 Pod 声明式文件方式创建 Pod&#xff0c;支持 yaml 和 json 1&…...

【Taro】初识 Taro

笔记来源&#xff1a;编程导航。 概述 Taro 官方文档&#xff1a;https://taro-docs.jd.com/docs/ &#xff08;跨端开发框架&#xff09; Taro 官方框架兼容的组件库&#xff1a; taro-ui&#xff1a;https://taro-ui.jd.com/#/ &#xff08;最推荐&#xff0c;兼容性最好&…...

【设计模式-备忘录】

备忘录模式&#xff08;Memento Pattern&#xff09;是一种行为型设计模式&#xff0c;用于保存对象的内部状态&#xff0c;以便在将来某个时间可以恢复到该状态&#xff0c;而不暴露对象的内部实现细节。备忘录模式特别适合在需要支持撤销&#xff08;Undo&#xff09;操作的应…...

【数据结构】排序算法系列——快速排序(附源码+图解)

快速排序 接下来我们将要介绍的是排序中最为重要的算法之一——快速排序。 快速排序&#xff08;英语&#xff1a;Quicksort&#xff09;&#xff0c;又称分区交换排序&#xff08;partition-exchange sort&#xff09;&#xff0c;最早由东尼霍尔提出。快速排序通常明显比其…...

Arthas thread(查看当前JVM的线程堆栈信息)

文章目录 二、命令列表2.1 jvm相关命令2.1.2 thread&#xff08;查看当前JVM的线程堆栈信息&#xff09;举例1&#xff1a;展示[数字]线程的运行堆栈&#xff0c;命令&#xff1a;thread 线程ID举例2&#xff1a;找出当前阻塞其他线程的线程 二、命令列表 2.1 jvm相关命令 2.…...

Tomcat_WebApp

Tomcat的目录的介绍 /bin&#xff1a; 这个目录包含启动和关闭 Tomcat 的脚本。 startup.bat / startup.sh&#xff1a;用于启动 Tomcat&#xff08;.bat 文件是 Windows 系统用的&#xff0c;.sh 文件是 Linux/Unix 系统用的&#xff09;。shutdown.bat / shutdown.sh&#xf…...

代码随想录算法训练营Day10

150. 逆波兰表达式求值 力扣题目链接&#xff1b;. - 力扣&#xff08;LeetCode&#xff09; Collection——Deque——LInkedList类 class Solution {public int evalRPN(String[] tokens) {Deque<Integer> myquenew LinkedList<>();for(String a:tokens){if(a.…...

十个服务器中毒的常见特征及其检测方法

服务器作为企业的核心资源&#xff0c;其安全性至关重要。一旦服务器被病毒入侵&#xff0c;不仅会影响系统的正常运行&#xff0c;还可能导致数据泄露等严重后果。以下是十种常见的服务器中毒特征及其检测方法。 1. 系统性能下降 病毒常常占用大量的CPU和内存资源&#xff0…...