【Mybits-Plus】拦截器的学习和使用,以及如何实现数据权限

news2024/11/17 15:36:08

【Mybits-Plus】拦截器的学习和使用目录标题

  • 常规处理数据权限的话Mybits需要对Mybits\Mybits-plus拦截器了解
    • 1.基础知识学习
    • 2.各种场景--实战案例

常规处理数据权限的话Mybits需要对Mybits\Mybits-plus拦截器了解

1.基础知识学习

(请自行学习如下内容,后续才能根据各种需求灵活调整满足场景的合适方案)

Mybatis——拦截器Interceptor

MyBatis 插件之拦截器(Interceptor)

Mybatis——执行流程及关键代码走读

Mybatis-Plus入门系列(3)- MybatisPlus之数据权限插件DataPermissionInterceptor

2.各种场景–实战案例

场景——数据加密(二)Mybatis拦截器
MyBatis实现SQL占位符替换

1. 数据权限控制–若依 AOP方案:详情见若依github工程

        方案缺点:会被信息安全扫描判定为sql注入

在这里插入图片描述

2. 数据权限控制–我公司方案:

结合若依DataScope注解和 MP组建的DataPermissionInterceptor进行扩展:
核心如下:

package mscp.boot.starter.data.permission.annotation;

import java.lang.annotation.*;


/**
 * 数据权限过滤注解
 * 常用在Mapper的sql方法注解上
 *
 * @author
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataPermission {
    /**
     * 查询组织主体的别名
     */
    public String orgSubjectAlias() default "";

    /**
     * 组织字段的别名
     */
    public String orgFieldAlias() default "";

    /**
     * 查询个人主体的别名
     */
    public String userSubjectAlias() default "";

    /**
     * 用户字段的别名
     */
    public String[] userFieldAlias() default {};


}

package mscp.boot.starter.data.permission.handler;

import com.baomidou.mybatisplus.extension.plugins.handler.DataPermissionHandler;
import com.sf.mscp.system.service.framework.api.SsFrameworkUtils;
import lombok.extern.slf4j.Slf4j;
import mscp.boot.starter.data.permission.annotation.DataPermission;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.expression.operators.relational.ItemsList;
import net.sf.jsqlparser.schema.Column;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;

/**
 * 编写数据权限处理逻辑,对SQL进行拦截处理
 *
 * @author 01392068
 * @date 2023/07/13 14:00
 **/
@Slf4j
public class MSCPDataPermissionHandler implements DataPermissionHandler {


    private static final String COUNT_SUFFIX = "_COUNT";


    /**
     * @param where             原SQL Where 条件表达式
     * @param mappedStatementId Mapper接口方法ID
     *                          <p>
     *                          eg:id=com.sf.nmrm.manage.common.mapper.NmrmDictionaryMapper.selectList
     * @return
     */
    @Override
    public Expression getSqlSegment(Expression where, String mappedStatementId) {
        log.info("=========================== start MyDataPermissionHandler");
        // 1. 模拟获取登录用户、系统模块信息

        //TODO 替换获取当前系统和人员信息
        String empCode = "002321";
        String sysType = "mscp_service";

        // 2.一级判定逻辑
        // 无当前用户相关信息间接认为是:识别开放接口、job任务不做数据权限拦截
        if (StringUtils.isAnyBlank(empCode, sysType)) {
            return null;
        }
        // 超管和所有组织 不做拦截
        if (SsFrameworkUtils.isSuperAdmin(empCode, sysType) || SsFrameworkUtils.isUserDataScopeContainsAll(empCode, sysType)) {
            return null;
        }


        // 3. 二级拦截逻辑
        // 3.1通过登录用户,从用户信息中获取ORG_ID
        //(1) user->sys_user_role->role_id (多个)
        //(2) role_id->sys_role->data_scope (多个)
        //(3) role_id->sys_role_org->org_id (多个)
        Set<Long> permissionOrgIds = SsFrameworkUtils.getUserDataPermissionOrgIds(empCode, sysType);

        List<String> dataScopes = Arrays.asList("1", "2", "3", "4");

        // 3.2. 通过 id 获取到 Dao 层类的全限定名称,然后反射获取 Class 对象 和Method

        String className = mappedStatementId.substring(0, mappedStatementId.lastIndexOf("."));
        String methodName = mappedStatementId.substring(mappedStatementId.lastIndexOf(".") + 1);
        // 分页插件会生成一个count语句,这个语句的参数也要做处理
        if (methodName.endsWith(COUNT_SUFFIX)) {
            methodName = methodName.substring(0, methodName.lastIndexOf(COUNT_SUFFIX));
        }
        // 动态加载类并获取类中的方法
        Method[] methods = new Method[0];
        try {
            methods = Class.forName(className).getMethods();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }


        // 4.根据注解添加过滤逻辑
        // (1). 遍历 Dao 层类的方法
        // 遍历类的所有方法并找到此次调用的方法,拼装permissionExpressions

        List<Expression> permissionExpressions = getExpressionsFromClassMethod(empCode, permissionOrgIds, methodName, methods);
        //(2)转换 permissionExpressions(List) to  OrExprssion
        if (CollectionUtils.isNotEmpty(permissionExpressions)){
            Expression orExpression = permissionExpressions.stream()
                    .reduce((e1, e2) -> new OrExpression(e1, e2))
                    .orElse(null);
            return new AndExpression(where, orExpression);
        }else{
            //TODO warning
            return where;
        }

    }

    /**
     * 根据注解和个人信息获取List Expression
     *
     * @param empCode 工号
     * @param permissionOrgIds 组织集合
     * @param methodName
     * @param methods
     * @return java.util.List<net.sf.jsqlparser.expression.Expression>
     */
    @NotNull
    private List<Expression> getExpressionsFromClassMethod(String empCode, Set<Long> permissionOrgIds, String methodName, Method[] methods) {
        List<Expression> permissionExpressions = new ArrayList<Expression>();
        for (Method method : methods) {
            if (method.getName().equals(methodName) && method.isAnnotationPresent(DataPermission.class)) {

                // 获取方法上的注解以及注解对应的参数
                DataPermission permissionAnnotation = method.getAnnotation(DataPermission.class);

                // 支持数据权限过滤
                String orgSubjectAlias = permissionAnnotation.orgSubjectAlias();
                String orgFieldAlias = permissionAnnotation.orgFieldAlias();
                String userSubjectAlias = permissionAnnotation.userSubjectAlias();
                String[] userFieldAlias = permissionAnnotation.userFieldAlias();
                if (CollectionUtils.isNotEmpty(permissionOrgIds) && StringUtils.isNotBlank(orgFieldAlias)) {
                    Column orgColumnInfo = null;
                    if (StringUtils.isNotBlank(orgSubjectAlias)) {
                        orgColumnInfo = new Column(String.format("%s.%s", orgSubjectAlias, orgFieldAlias));
                    } else {
                        orgColumnInfo = new Column(orgFieldAlias);
                    }
                    //  order_tbl.dept_id IN ('2', '3', '4', '5')
                    InExpression inExpression = new InExpression(orgColumnInfo,
                            (ItemsList) permissionOrgIds);
                    permissionExpressions.add(inExpression);
                }
                if (ArrayUtils.isEmpty(userFieldAlias)) {
                    for (String empColumn : userFieldAlias) {
                        Column empColumnInfo = null;
                        if (StringUtils.isNotBlank(userSubjectAlias)) {
                            empColumnInfo = new Column(String.format("%s.%s", userSubjectAlias, empColumn));
                        } else {
                            empColumnInfo = new Column(empColumn);
                        }
                        // order_tbl.user_code = 'xxxxx'
                        EqualsTo equalsTo = new EqualsTo();
                        equalsTo.setLeftExpression(empColumnInfo);
                        equalsTo.setRightExpression(new StringValue(empCode));
                        permissionExpressions.add(equalsTo);
                    }
                }
                break;
            }
        }
        return permissionExpressions;
    }
}

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

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

相关文章

C++中随机数的使用总结

随机数 rand() 随机数发生器 #include <stdio.h> #include <unistd.h> #include <iostream> #include <string>using namespace std;int main(int argc, char *argv[]) {cout << "this is main()" << endl;cout << rand…

2023-07-14 monetdb-嵌入mysql-sql层交互-设计-分析

摘要: SQL层的兼容工作,时间紧,任务重,协议多,命令多,功能多,牵扯到数据一致性又万分紧张,需要非常谨慎,当前列了一些下一步的工作的设计分析. 主要是涉及sql层接口的兼容, innodb与monetdb数据的一致性, 查询和写入两个不同侧面的处理. monetdb嵌入mysql分析: 导图版…

C语言中定义和声明的区别

声明(declaration)与定义(definition) 为了使不同的文件都可以访问同一个变量&#xff0c;C会区 分变量的声明和定义。 变量的定义会为这个变量分配存储空间&#xff0c;并且 可能 会为其指定一个初始化的值&#xff0c; 一个变量的定义有且 仅有一处。 定义实际上是一种特殊…

七种最新群智能优化算法(NOA、LSO、SWO、ZOA、EVO、KOA、GRO)求解23个基准测试函数(含参考文献及MATLAB代码)

一、七种算法简介 &#xff08;1&#xff09;星雀优化算法NOA 星雀优化算法(Nutcracker optimizer algorithm,NOA)由Mohamed Abdel-Basset等人于2023年提出&#xff0c;该算法模拟星雀的两种行为&#xff0c;即&#xff1a;在夏秋季节收集并储存食物&#xff0c;在春冬季节搜…

路径规划算法:基于浣熊优化的路径规划算法- 附代码

路径规划算法&#xff1a;基于浣熊优化的路径规划算法- 附代码 文章目录 路径规划算法&#xff1a;基于浣熊优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要&#xff1a;本文主要介绍利用智能优化算法浣熊…

调频连续波(FMCW)波形设计、真实道路场景仿真及汽车自适应巡航控制信号处理(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 以下是关于调频连续波&#xff08;FMCW&#xff09;波形设计、真实道路场景仿真以及汽车自适应巡航控制信号处理的概述&#x…

【问题解决】VSCode 远程安装插件特别慢

【问题描述】 我要配置 VSCode WSL 的开发环境&#xff0c;需要在 WSL 里也装上 C、CMake 系列的插件&#xff0c;如下图的直接下载方式特别慢&#xff1a; 【解决方法】 先去网站下载插件&#xff1a;https://marketplace.visualstudio.com/&#xff0c;后缀名&#xff1a;…

【DFS】LeetCode 17. 电话号码的字母组合

Halo&#xff0c;这里是Ppeua。平时主要更新C语言&#xff0c;C&#xff0c;数据结构算法…感兴趣就关注我bua&#xff01; 目录 题目:示例: 题解:代码实现: 题目: 示例: 题解: 这是一道全排列的问题,先来看看示例. 看第一个示例:输入"23",其对应的是"abc&qu…

【GIT】如何在GitHub上向一个开源项目贡献代码?

如何在GitHub上向一个开源项目贡献代码 参考链接&#xff1a;https://www.bilibili.com/video/BV1WC4y1a76G/?p1&t175 一、fork目标仓库的代码 必须先fork别人的代码到自己仓库之后才能修改代码&#xff0c;不能直接修改别人的代码。 这样就fork到自己的仓库了。 二、…

学习react,复制一个civitai-更新2

更新内容 耗时一个礼拜左右&#xff0c;增加了个新界面&#xff1a;模型图片详情界面。 看看效果图吧&#xff1a; 功能介绍 操作&#xff1a;在模型详情界面点击一个图片&#xff0c;就能到图片详情界面 1.点击哪个图片&#xff0c;就会展示哪个&#xff0c;同时还会更新图…

【C++】面试基础搬运

c/c c三大特性 封装 最开始接触代码是C语言&#xff0c;那么开始写一些逻辑代码的时候会很麻烦&#xff0c;因为你要在函数中定义变量&#xff0c;然后按顺序写对应的逻辑&#xff0c;接着可以将逻辑封装成函数。当时会感觉很麻烦&#xff0c;因为很散装&#xff0c;知道后面…

docker-compose 常用命令(附 docker-compose 的安装教程)

本文目录 1. docker-compose 介绍1. docker-compose 简介2. docker-compose 安装3. docker-compose 卸载 2. docker-compose 常用命令1. docker-compose 命令格式2. docker-compose up3. docker-compose ps4. docker-compose stop5. docker-compose down6. docker-compose logs…

虚幻引擎程序化资源生成框架PCG 之Gather(收集)、Merge(合并)、Union(并集)

有朋友询问&#xff1a;Gather&#xff08;收集&#xff09;、Merge&#xff08;合并&#xff09;、Union&#xff08;并集&#xff09;这三个运算节点&#xff0c;看名字有些相似&#xff0c;究竟区别是什么&#xff1f;目前还没有详细的官方文档&#xff0c;所以今天老王结合…

电脑城逐渐衰退甚至消失,究竟是好是坏呢?

在过去很长一段时间里&#xff0c;想要购买电子设备都逃不开一个叫“电脑城”的地方&#xff0c;那里鱼龙混杂良莠不齐&#xff0c;是令许多人记忆深刻分外难忘之处。但是随着时代发展电商兴起&#xff0c;采用传统线下销售的电脑城却逐渐衰退甚至面临消失。 电脑城曾经是很多…

【2】Spring手写模拟-依赖注入、初始化、AOP

首先回顾一下我们之前的流程ApplicationContext 构造方法 获取Spring配置类ComponetScan 注解&#xff0c;扫描包&#xff0c;获取其路径及其对应的字节码文件逐个扫描字节码文件 利用字节码文件获取类&#xff0c;查看是否包含Componet 注解&#xff0c;并获取或者生成BeanNa…

vv、vt 埋点上报自动化文档

vv、vt 埋点自动化文档 文章目录 vv、vt 埋点自动化文档一、项目简介二、环境搭建Airtest官方参考文档AirtestIDE下载Python环境安装Airtest安装安卓环境IOS 环境iOS-Tagent 编译 设置代理 三、框架介绍四、实现原理4.1 框架流程图4.2 初始化配置4.2.1 读取mock server配置&…

sqli-labs

目录 Less1 首先来爆字段 联合注入 判断注入点 爆数据库名 爆破表名 information_schema information_schmea.tables group_concat() 爆破列名 information_schema.columns 爆值 SQLMAP 主要对sqli-labs 的深入学习 Less1 我们先看看源代码 <?php //includ…

山西电力市场日前价格预测【2023-07-16】

日前价格预测 预测明日&#xff08;2023-07-16&#xff09;山西电力市场全天平均日前电价为395.36元/MWh。其中&#xff0c;最高日前电价为510.19元/MWh&#xff0c;预计出现在20: 00。最低日前电价为309.52元/MWh&#xff0c;预计出现在13: 15。 价差方向预测 1&#xff1a;实…

如何从零开始搭建公司自动化测试框架?

搭建的自动化测试框架要包括API测试&#xff0c;UI测试&#xff0c;APP测试三类。以上三类其实可以简化为两类&#xff0c;那就是&#xff1a; 1&#xff09;接口自动化测试框架搭建 2&#xff09;UI自动化测试框架搭建。 没问题&#xff0c;安排&#xff0c;且是手把手教你如何…

Next.js框架入门笔记

内置组件 ‘pages/_document.js’ 文件&#xff0c;自定义document DOC&#xff1a; https://www.nextjs.cn/docs/advanced-features/custom-document <Head>组件 <Head>是一个内置在 Next.js 中的 React 组件。它允许您修改页面的<head>。 Docs: https:/…