当前位置: 代码网 > it编程>编程语言>Java > mybatis plus数据权限插件在项目中的使用方式

mybatis plus数据权限插件在项目中的使用方式

2026年01月08日 Java 我要评论
前言平时开发中遇到根据当前用户的角色,只能查看数据权限范围的数据需求。实现方案有两种,一是在开发初期就做好判断,但如果这个需求是中途加的,或不希望每个接口都加一遍,就可以方案二加拦截器的方式。在myb

前言

平时开发中遇到根据当前用户的角色,只能查看数据权限范围的数据需求。实现方案有两种,一是在开发初期就做好判断,但如果这个需求是中途加的,或不希望每个接口都加一遍,就可以方案二加拦截器的方式。在mybatis执行sql前修改语句,限定where范围。

当然拦截器生效后是全局性的,如何保证只对需要的接口进行拦截和转化,就可以应用注解进行识别

因此具体需要哪些步骤就明确了:

  1. 创建注解类
  2. 创建处理类,获取数据权限 sql 片段,设置条件
  3. 将拦截器加到mybatis-plus插件中

数据权限插件

datapermissioninterceptor

插件原理和租户插件类似动态拦截执行 sql 然后拼接权限部分 sql片段 , 该插件一直是免费开源的,企业高级特性-数据范围功能也是基于该原理实现,只不过添加了注解支持。

使用datapermissionhandler

/**
 * 数据权限处理器
 *
 * @author hubin
 * @since 3.4.1 +
 */
public interface datapermissionhandler {

    /**
     * 获取数据权限 sql 片段
     *
     * @param where             待执行 sql where 条件表达式
     * @param mappedstatementid mybatis mappedstatement id 根据该参数可以判断具体执行方法
     * @return jsqlparser 条件表达式
     */
    expression getsqlsegment(expression where, string mappedstatementid);
}

先定义一个注解:

@target({elementtype.method, elementtype.type})
@retention(retentionpolicy.runtime)
public @interface datascope {
    /**
     * 表前缀
     */
    string tablealis() default "";
    /**
     * 机构前缀
     */
    string orgalis() default "org_id";

    /**
     * 是否生效
     */
    boolean enabled() default true;
}

新建一个orgscopehandler,通过实现以上接口:

 @override
    public expression getsqlsegment(expression where, string mappedstatementid) {

        try {
            class<?> clazz = class.forname(mappedstatementid.substring(0, mappedstatementid.lastindexof(".")));
            string methodname = mappedstatementid.substring(mappedstatementid.lastindexof(".") + 1);
            method[] methods = clazz.getdeclaredmethods();
            for (method method : methods) {
                datascope annotation = method.getannotation(datascope.class);
                if (objectutils.isnotempty(annotation)
                        && (method.getname().equals(methodname) || (method.getname() + "_count").equals(methodname))) {
                    if (annotation.enabled()) {

                        return datascopefilter(annotation, where);
                    }

                }
            }
        } catch (classnotfoundexception e) {
            e.printstacktrace();
        }
        return where;
    }

在项目中的mybatisplusconfig中加上自定义的orgscopehandler:

 mybatisplusinterceptor interceptor = new mybatisplusinterceptor();
 // 数据权限
        interceptor.addinnerinterceptor(new datapermissioninterceptor(new orgscopehandler()));

getsqlsegment中的实现是遍历mybatis mappedstatement类的所有方法,对每一个方法执行以下操作:

获取该方法上的 datascope 注解。

检查注解是否存在且不为空(objectutils.isnotempty(annotation))。

检查方法名是否与之前获取的 methodname 相同,或者是方法名后加上 _count 后缀的情况。

如果以上条件都满足,再检查 datascope 注解的 enabled() 属性是否为 true。

如果找到了匹配的方法并且注解启用,那么调用 datascopefilter 方法,传入该方法上的 datascope 注解和 where 参数,进行数据范围过滤。

自定义的datascopefilter方法

    /**
     * 根据注解中的内容构造 拼接sql,用于数据权限过滤
     *
     * @param datapermission 数据权限注解,用于指定表和组织别名等信息
     * @param where 原始sql的where条件表达式
     * @return 返回拼接后的sql表达式,添加了数据权限的过滤条件
     */
    private expression datascopefilter(datascope datapermission, expression where) {
        // 获取当前登录的用户
        nutuser user = securityutil.getnutuser();

        string tablealias = datapermission.tablealis(); // 表别名
        string orgalias = datapermission.orgalis(); // 组织别名
        // 构造数据过滤的in表达式
        inexpression inexpression = new inexpression();
        // 设置左边的字段表达式,根据表和组织别名构建
        inexpression.setleftexpression(buildcolumn(tablealias, orgalias));
        
        // 使用jsqlparser解析组织id的查询语句
        ccjsqlparsermanager parsermanager = new ccjsqlparsermanager();
        statement statement;
        try {
            // 构造查询当前用户所属组织及其子组织id的sql语句
            stringreader statementreader = new stringreader(
                    "select id from sys_org where path like '%" + user.getorgid() + "%'");
            statement = parsermanager.parse(statementreader);
        } catch (jsqlparserexception e) {
            // 处理解析异常
            throw new runtimeexception(e);
        }
        select selectstatement = (select) statement;
        subselect subselect = new subselect();
        subselect.setselectbody(selectstatement.getselectbody()); // 设置子查询的查询体
        inexpression.setrightexpression(subselect); // 设置右边的子查询表达式

        // 如果原有where条件不为空,则将数据权限过滤条件和原有条件用and连接
        return objectutils.isnotempty(where) ? new andexpression(where, new parenthesis(inexpression)) : inexpression;
    }

buildcolumn方法:

 /**
     * 构建column
     *
     * @param tablealias 表别名
     * @param columnname 字段名称
     * @return 带表别名字段
     */
    public static column buildcolumn(string tablealias, string columnname) {
        if (stringutils.isnotempty(tablealias)) {
            columnname = tablealias + "." + columnname;
        }
        return new column(columnname);
    }

使用:

  /**
     * 分页查询
     *
     * @param page  page
     * @param query query
     */
    @datascope(tablealis = "item")
    page<infobo> selectpagequery(page page, @param("query") handlequery query);

结果是实际的sql上将拼接上:

and (org_id in (select id from sys_org where path like concat('%', 1, '%')))

ccjsqlparsermanager类解析

ccjsqlparsermanager 类是 jsqlparser 库中的一个关键类,它主要用于管理和执行 sql 语句的解析操作。

jsqlparser 是一个 java 库,用于解析 sql 语言,并将其转化为可遍历、操作和修改的 java 对象模型。

ccjsqlparsermanager 主要功能如下:

  1. 初始化解析器资源:该类负责初始化和管理 jsqlparser 解析器所需的资源。
  2. 解析 sql 语句:它可以接受字符串形式的 sql 查询,并通过 parse() 方法将其转换成 statement 对象,这是一个抽象类,代表了 sql 语句的不同类型(如 select, update, delete, insert 等)。
  3. 处理并发请求:ccjsqlparsermanager 可能会实现资源池或其他机制来更有效地处理多个并发的 sql 解析请求。
    例如,在上述代码片段中:
ccjsqlparsermanager parsermanager = new ccjsqlparsermanager();
statement statement;
try {
    stringreader statementreader = new stringreader("select id from sys_org where path like '%" + user.getorgid() + "%'");
    statement = parsermanager.parse(statementreader);
} catch (jsqlparserexception e) {
    throw new runtimeexception(e);
}

这段代码就是创建了一个 ccjsqlparsermanager 实例,然后使用它来解析一个 sql 语句,这个 sql 语句是为了查询当前用户及其子组织的 id。

通过解析得到的 statement 对象可以进一步被分析或修改,以便进行动态 sql 生成、权限检查、sql 优化等各种操作。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com