数据权限——Mybatis拦截器实现

2023-11-07

一、需求背景介绍

1、需求介绍
需要实现数据权限管理,包含角色:普通用户、组长、管理员。其中普通用户只能看到自己创建的项目,组长能看到自己所管理的普通用户创建的项目,管理员能看到所有项目。相关表为:项目表(包含责任人owner字段,owner所属组group字段)、用户表(包含组id)、组长信息表、管理员表。
2、方案设计
采用Mybatis拦截器,在请求查询sql后拼条件。
(1)如果当前用户为普通用户,查询项目时拼上条件owner=user;
(2)如果当前用户为组长,查找当前user所管理的组list,拼上条件group in (…);
(3)如果当前用户为管理员,不拼额外条件。

二、Mybatis拦截器介绍

关于Mybatis拦截器,网上有不少博客介绍,本文不做详细解释,这里贴一个相关链接Mabatis拦截器
Mybatis拦截器并不是每个对象里面的方法都可以被拦截的。Mybatis拦截器只能拦截Executor、ParameterHandler、StatementHandler、ResultSetHandler四个对象里的方法。

三、代码

本文采用拦截StatementHandler里的prepare方法。(不会影响分页结果。代码做了删减,无法直接使用,可做参考)

import java.io.StringReader;
import java.sql.Connection;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Properties;

import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.StatementType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.stereotype.Component;

import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.handlers.AbstractSqlParserHandler;
import com.google.common.collect.Lists;

import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Parenthesis;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.parser.CCJSqlParserManager;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.update.Update;

@Slf4j
@Intercepts({@Signature(
        type = StatementHandler.class,
        method = "prepare",
        args = {Connection.class, Integer.class})})
@Component
public class AuthInterceptor extends AbstractSqlParserHandler implements Interceptor {

    public AuthInterceptor() {
    }

    /**
     * 自定义的逻辑处理
     */
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());
        //映射工具
        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
        //中间去判断多层
        this.sqlParser(metaObject);
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        //获取到执行的sql
        BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
        CCJSqlParserManager parserManager = new CCJSqlParserManager();

        //select
        if (SqlCommandType.SELECT == mappedStatement.getSqlCommandType() && StatementType.CALLABLE != mappedStatement
                .getStatementType()) {
            Select select = (Select) parserManager.parse(new StringReader(boundSql.getSql()));
            PlainSelect selectBody = (PlainSelect) select.getSelectBody();
            // 获取表名
            String tableName = ((Table) selectBody.getFromItem()).getName();
            // 这里对需要拦截的表做下筛选
            if (StringUtils.equalsAny(tableName, "project", "supplier")) {
                log.info("权限拦截...");
                String aliasName = ""; // 表别名
                try {
                    aliasName = selectBody.getFromItem().getAlias().getName() + ".";
                } catch (Exception ignored) {
                }
                // 需要拼的条件
                String whereSql = getNewSql(tableName, aliasName);
                // 把要拼的条件设置进去
                if (StringUtils.isNotEmpty(whereSql)) {
                    Expression whereExpression = CCJSqlParserUtil.parseCondExpression(whereSql);
                    if (null != selectBody.getWhere()) {
                        selectBody.setWhere(new AndExpression(selectBody.getWhere(), new Parenthesis(whereExpression)));
                    } else {
                        selectBody.setWhere(whereExpression);
                    }
                }
                metaObject.setValue("delegate.boundSql.sql", selectBody.toString());
            }
        }
        return invocation.proceed();
    }

    private String getNewSql(String tableName, String aliasName) {
        StringBuilder whereSql = new StringBuilder();
        UserInfo userInfo = UserUtil.getCurrentUser();
        String userRole = userInfo.getRole(); // 当前用户角色,逗号分隔
        String groupList = userInfo.getGroup(); // 当前用户所管理的组,逗号分隔
        switch (tableName) {
            case "project":
                if (userRole.contains(Role.manager.getCode())) {
                    
                } else if (userRole.contains(Role.leader.getCode())) {
                    List<String> purchaseTeamList = Arrays.asList(sourcingProjectScope.split(","));
                    whereSql.append("(").append(aliasName).append("group in ('").append(String.join("','",
                            purchaseTeamList)).append("')");
                } else {
                    whereSql.append(aliasName).append("owner = '").append(userInfo.getUsername()).append("'");
                }
                break;
            case "supplier":
                /*
                如果拦截的是别的表,需要做什么操作,可以自己加。
                 */
                break;
            default:
                return null;
        }
        return whereSql.toString();
    }

    @Override
    public Object plugin(Object target) {
        return target instanceof StatementHandler ? Plugin.wrap(target, this) : target;
    }

    @Override
    public void setProperties(Properties properties) {

    }
}

四、关于操作权限

数据权限做了简化处理,实际比上文介绍的复杂。比如组长及管理员不能查看普通用户新建、取消等状态的项目,组长及管理员只有只读权限没有编辑权限等。这个就涉及到操作权限了。
方案:将页面上的按钮、服务、Tab等资源赋予一个唯一资源id。对于一个用户所拥有的角色,该角色具有的操作权限资源集合作为A。对于只读角色,所能操作的资源集合作为B。那么一个用户进入到项目,owner字段如果不是当前user,则该用户不能编辑,只有只读权限,该用户所拥有的资源集合就为A∩B。将该资源集合返回给前端,前端给予控制展示。
另外,如果系统只允许用户表中存在的用户使用,那么可以在拦截器中做一层过滤,不是用户表中的用户,返回403跳转无权限页。在网关也可以做一层操作权限控制,如果当前用户所请求的uri不在该用户所能操作的资源uri集合A中,则直接拒绝。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

数据权限——Mybatis拦截器实现 的相关文章

  • 在java中将StreamWriter转换为OutputStream?

    我正在尝试使用 System setOut 将 System out 重定向到字符串 它需要一个 PrintStream 有什么方法可以将 StringWriter 转换为 Stream 以便我可以将其传递给 setOut 吗 你不能完全这
  • 如何在java swing中的每个页面中打印带有页脚的整个JPanel

    好吧 这可能很简单 但想不通 我有一个包含 JTable 的 JPanel JTable 包含很少的行 有时更多 因为我推入其中的表模型取决于数据库 但是 我不使用任何包含 JTable 的 JScolpane 因此 当 JTable 包含
  • 以编程方式将 PEM 证书导入 Java KeyStore

    我有一个由两个文件 crt 和 key 组成的客户端证书 我希望将其导入到 java KeyStore 中 然后在 SSLContext 中使用 以通过 Apache 的 HTTPClient 发送 HTTP 请求 但是 我似乎找不到一种以
  • Java/JAXB:将具有相同名称但不同属性值的 XML 元素解组到不同的类成员

    我正在尝试根据其属性之一将具有多个 Fields 元素的 XML 解析为不同的类成员 这是 XML
  • 如何在Spring的applicationContext.xml中指定默认范围来请求范围?

    我想让所有 bean 请求默认作用域 但是 Spring 文档说默认作用域是 Singleton 第 3 4 1 和 3 4 2 节http static springsource org spring docs 2 5 x referen
  • java“void”和“非void”构造函数

    我用 java 编写了这个简单的类 只是为了测试它的一些功能 public class class1 public static Integer value 0 public class1 da public int da class1 v
  • 方法不必要地被调用?

    我有一个 BaseActivity 它可以通过其他所有活动进行扩展 问题是 每当用户离开 暂停 活动时 我都会将音乐静音 我也不再接听电话 问题是 onPause每当用户在活动之间切换时就会被调用 这意味着应用程序不必要地静音和停止tele
  • 如何在 OpenAPI 3.0 中定义字节数组

    我正在将 API 从 Swagger 2 0 迁移到 OpenAPI 3 0 在 DTO 中 我有一个指定为字节数组的字段 Swagger 对 DTO 的定义 Job type object properties body type str
  • 如何在具有动态列的表中插入值 Jdbc/Mysql

    我想在具有动态列的表中添加值 我设法创建一个包含动态列的表 但我不知道如何插入数据 Create Table sql CREATE TABLE MyDB myTable level INTEGER 255 int columnNumber
  • 如何模拟一个方面

    我目前正在使用aspectj 开发一些监控工具 因为这个工具应该是技术独立的 尽可能 所以我没有使用 Spring 进行注入 但我希望我的方面能够经过单元测试 方面示例 Aspect public class ClassLoadAspect
  • 会话 bean 中的 EntityManager 异常处理

    我有一个托管无状态会话 bean 其中注入了 EntityManager em 我想做的是拥有一个具有唯一列的数据库表 然后我运行一些尝试插入实体的算法 但是 如果实体存在 它将更新它或跳过它 我想要这样的东西 try em persist
  • 如何自定义JProgressBar?

    我正在制作一个启动器 我想要一个自定义的进度栏 我已经做了一些研究 并且可以使用 JavaFX 从未用它做过任何事情 并且可以通过替换 UI 来实现 我正在寻找一个具有圆形边缘和圆形填充的酒吧 像这样的事情 package gui impo
  • 当容器大小更改时,JTable 仅调整选定列的大小

    对于面板内的 JTable 如果面板变大 我如何将额外的空间仅分配给某些列 在我的例子中 分配给最后一列 尽管提供 第 3 4 列和8 将获得额外的空间 我想允许用户手动更改所有列的列大小 我尝试了 table setAutoResizeM
  • @TestPropertySource 不适用于 Spring 1.2.6 中使用 AnnotationConfigContextLoader 的 JUnit 测试

    似乎我在 Spring 4 1 17 中使用 Spring Boot 1 2 6 RELEASE 所做的任何事情都不起作用 我只想访问应用程序属性并在必要时通过测试覆盖它们 无需使用 hack 手动注入 PropertySource 这不行
  • java中的比较器链

    正在阅读Oracle 关于接口的 Java 教程 https docs oracle com javase tutorial java IandI createinterface html其中给出了一个例子Card 打牌 我试图理解接口中的
  • Android 中的字符串加密

    我正在使用代码进行加密和加密 它没有给出字符串结果 字节数组未转换为字符串 我几乎尝试了所有方法将字节数组转换为字符 但没有给出结果 public class EncryptionTest extends Activity EditText
  • Java SE + Spring Data + Hibernate

    我正在尝试使用 Spring Data Hibernate 启动 Java SE 应用程序 并且到目前为止已经完成了以下操作 配置文件 Configuration PropertySource classpath hibernate pro
  • 监控 Java 应用程序上的锁争用

    我正在尝试创建一个小基准 在 Groovy 中 以显示几个同步方法上的高线程争用 当监控自愿上下文切换时 应该会出现高争用 在 Linux 中 这可以通过 pidstat 来实现 程序如下 class Res private int n s
  • Java 9 中紧凑字符串和压缩字符串的区别

    有什么优点紧凑的字符串 http openjdk java net jeps 254JDK9 中的压缩字符串 压缩字符串 Java 6 和紧凑字符串 Java 9 都有相同的动机 字符串通常实际上是 Latin 1 因此浪费了一半的空间 和
  • 如何使用 Spring AOP 建议静态方法?

    在执行类的静态方法之前和之后需要完成一些日志记录 我尝试使用 Spring AOP 来实现这一点 但它不起作用 而对于正常方法来说它起作用 请帮助我理解如何实现这一点 如果可以使用注释来完成 那就太好了 也许您应该在使用 Spring AO

随机推荐

  • RecyclerView 显示不全问题

    按道理ItemCount的数量应该是跟onCreateViewHolder调用次数一致的 但在实际开发中遇到不一致的情况 可能出现的问题 某一个item的高度太高 导致其他的item无法在可显示的范围内 RecyclerView只有在显示范
  • c++类模板案例

    案例要求 创建一个数组类模板 可以对内置数据类型以及自定义数据类型的数据进行存储 将数组中的数据存储到堆区 构造函数中可以传入数组的容量 提供对应的拷贝构造函数以及operator 防止浅拷贝问题 提供尾插法和尾删法对数组中的数据进行增加和
  • JS/jq 实现复选框默认选中

    div div
  • Java-方法的重写(Overriding)和重载(Overloading)

    方法的重载 Overloading 方法的重载是指一个类中定义多个同名的方法 但方法的参数类型 参数个数或参数顺序不同 重载的规则 相同的方法名 参数列表不同 类型 个数 顺序的不同 最好相同的返回值类型 可以不同 可以抛出不同的异常 可以
  • 网页三剑客之 HTML

    本章开始我们来介绍一下网页前端部分 我们只是简单的介绍一些常用的各种标签 其目的在于为我们后面的项目做准备 我们并不要求能完全掌握前端的语法 但是在见到以后能够认识这些代码就可以了 想走后端开发的 前端不需要多么熟悉 毕竟在各个企业中前后端
  • 四种常用的自动化测试框架

    一直想仔细研究框架 写个流水账似的测试程序不难 写个低维护成本的测试框架就很难了 所以研究多种测试框架还是很有必要的 知道孰优孰劣 才能在开始编写框架的时候打好基础 今天读到了KiKi Zhao的翻译文章 觉得很是不错 写了一点学习心得 有
  • Numpy简单入门

    概述 Numpy是高性能科学计算好数据分析的基础包 提供了矩阵运算的功能 在深度学习和数据分析领域广泛应用 使用 创建array数组 vector np asarray 1 2 3 4 创建zero数组 a np zeros 10 创建向量
  • FatFS文件系统的使用(STM32)

    目录 1 Fatfs相关文件 2 驱动测试 3 重试机制 4 实现较为复杂的文件操作 4 1 获取磁盘容量 4 2 遍历目录 获取文件大小和数量 以及查找 删除等操作 4 3 认识文件权限和错误码 1 Fatfs相关文件 fatfs的介绍这
  • 聊聊leader的自我修炼

    这是鼎叔的第十七篇原创文章 行业大牛和刚毕业的小白 都可以进来聊聊 欢迎关注本人专栏和微信公众号 敏捷测试转型 大量原创思考文章陆续推出 4月3号晚上 鼎叔作为嘉宾参与小道消息播客的直播分享节目 和主持人老徐和兔子畅谈测试管理的那些事 本文
  • 【廖雪峰python入门笔记】列表生成式

    1 生成列表 要生成list 1 2 3 4 5 6 7 8 9 10 我们可以用range 1 11 gt gt gt range 1 11 1 2 3 4 5 6 7 8 9 10 但如果要生成 1x1 2x2 3x3 10x10 怎么
  • Web系统大规模并发:电商秒杀与抢购

    http blog jobbole com 91754 一 大规模并发带来的挑战 在过去的工作中 我曾经面对过5w每秒的高并发秒杀功能 在这个过程中 整个Web系统遇到了很多的问题和挑战 如果Web系统不做针对性的优化 会轻而易举地陷入到异
  • Android图片加载优化方案

    1 前言 在电商APP中 图片在整个页面中占比最大 清晰高质量的图片能够明显提升转化率 但是APP运行环境错综复杂 往往我们会遇到 图片压缩导致模糊 列表加载长时间显示空白图 查看大图黑屏过久 甚至因为图片过大导致crash等 如下效果展示
  • Java编程思想课后练习题——第三章-操作符

    本文主要依据 Java编程思想 的示例及课后练习 通过个人编写 同时参考答案的代码写法 主要目的是用于自己熟悉编码风格 同时可以给更多人提供参考 只完成了部分练习 练习1 package 操作符 import static net mind
  • VScode安装

    1 下载安装VScode 下载地址 Visual Studio Code Code Editing Redefined 浏览器下载很慢 我们进入下载页面 复制下载链接 https az764295 vo msecnd net stable
  • ubunutu20.04 pycharm使用anaconda下环境(主要是pytorch)

    新建pycharm项目和anaconda环境 打开pycharm new project new environment using conda 修改环境名 项目名 python版本选择3 9 我的电脑pyTorch在3 9上跑通了 其他版
  • SAP应付模块详解

    本文介绍以下内容 应付模块的基础知识 主数据 供应商 发票处理 付款及清账 预付款 应付票据 其他特别总账业务 供应商余额查询 定期处理 月末及年末年初的操作 应付模块报表 应付模块设计的流程清单和方案要点 由于应付模块和应收模块在很多方面
  • LaTex 之 数学运算符号

    属于号 in 开根号 sqrt 求和符号 sum 积分符号 int min max 大于等于 小于等于号 导言区使用两个宏包 usepackage amsmath usepackage amssymb 大于等于号 geqslant or g
  • Code Review的亲身实践

    Code Review 中文叫代码审查 指的是完成了部分功能的代码开发之后 在代码真正合并到仓库主分支之前 邀请同事帮你进行代码的审核和检查 检查代码的质量 规范 设计等等方面的过程 代码审查的好处 知识共享 进行代码审查的好处很多 其中一
  • Elasticsearch入门初探-单机多节点集群

    实时搜索引擎Elasticsearch 简称ES 是一个基于Apache Lucene 的开源搜索引擎 无论在开源还是专有领域 Lucene可以被认为是迄今为止最先进 性能最好 这里分享Elasticsearch入门在单机下如何配置多节点集
  • 数据权限——Mybatis拦截器实现

    一 需求背景介绍 1 需求介绍 需要实现数据权限管理 包含角色 普通用户 组长 管理员 其中普通用户只能看到自己创建的项目 组长能看到自己所管理的普通用户创建的项目 管理员能看到所有项目 相关表为 项目表 包含责任人owner字段 owne