当前位置: 代码网 > it编程>编程语言>Java > Mybatis拦截器如何实现数据权限过滤

Mybatis拦截器如何实现数据权限过滤

2025年01月01日 Java 我要评论
背景现在的项目负责人去年年底离职,导致前期规划的数据过滤功能一直没有去实现。现在项目马上进入试运行时期了,需要根据用户数据权限的配置对数据进行过滤处理。如果一个个手动去mapper.xml文件中修改s

背景

现在的项目负责人去年年底离职,导致前期规划的数据过滤功能一直没有去实现。

现在项目马上进入试运行时期了,需要根据用户数据权限的配置对数据进行过滤处理。

如果一个个手动去mapper.xml文件中修改sql工作量太大了,后面我考虑通过mybatis对查询的sql进行处理。

基础知识

mybatis 拦截器介绍

interceptor接口源码解析

package org.apache.ibatis.plugin;  
  
import java.util.properties;  
  
public interface interceptor {  
	
    object intercept(invocation invocation) throws throwable;  

    default object plugin(object target) {  
        return plugin.wrap(target, this);  
    }  
    
    default void setproperties(properties properties) {  
    }  
}
  • intercept 方法
    这个方法是核心,当拦截到调用时会执行。invocation 对象包含了被拦截方法的所有信息,包括方法本身、参数、目标对象等。在这个方法中,你可以做任何预处理或后处理逻辑,然后通过调用 invocation.proceed() 来继续执行原方法,或者直接返回自定义的结果。
  • plugin方法
    这个方法用于决定是否对某个对象应用拦截器。如果返回 target,则表示不进行拦截;如果返回一个新的对象,则表示将使用这个新对象替代原有的对象,通常是在这里返回一个代理对象。
  • setproperties 方法
    用于设置拦截器的属性,这些属性可以在 mybatis 的配置文件中定义。

signature 注解源码解析

@documented  
@retention(retentionpolicy.runtime)  
@target({})  
public @interface signature {  
	class<?> type();

	string method();

	class<?>[] args();
}
  • type:表示目标对象的类型,
  • method:表示要拦截的目标方法的名字。
  • args:表示目标方法的参数类型列表。不同的 @signature 注解可能有不同的参数类型列表,这取决于具体的方法签名。

代码实战

实现一个类似与pagehelper的一个工具类,在本地线程变量中存储数据权限相关信息

public class dataaccessmethod {  
    private static final threadlocal<dataaccesstype[]> access_local = new threadlocal<>();  
  
  
    public dataaccessmethod() {  
  
    }  
    public static void setlocalaccess(dataaccesstype... accesstype) {  
        access_local.set(accesstype);  
    }  
  
    public static dataaccesstype[] getlocalaccess() {  
        return access_local.get();  
    }  
  
    public static void clearlocalaccess() {  
        access_local.remove();  
    }  
  
    public static void accessdata(dataaccesstype... accesstype) {  
        setlocalaccess(accesstype);  
    }  
}

实现 interceptor接口对sql进行增强处理

@intercepts({@signature(  
        type = executor.class,  
        method = "query",  
        args = {mappedstatement.class, object.class, rowbounds.class, resulthandler.class}  
), @signature(  
        type = executor.class,  
        method = "query",  
        args = {mappedstatement.class, object.class, rowbounds.class, resulthandler.class, cachekey.class, boundsql.class}  
)})  
@slf4j  
@component  
public class dataaccessfilterinterceptor implements interceptor {  
  
  
    @override  
    public object intercept(invocation invocation) throws throwable {  
        if (skip()) {  
            return invocation.proceed();  
        }  
        mappedstatement statement = (mappedstatement) invocation.getargs()[0];  
        object parameter = invocation.getargs()[1];  
        boundsql boundsql = statement.getboundsql(parameter);  
        string originalsql = boundsql.getsql();  
        object parameterobject = boundsql.getparameterobject();  
        string sql = addtenantcondition(originalsql, "1", "222");  
        log.info("原sql:{}, 数据权限替换后的sql:{}", originalsql, sql);  
  
        boundsql newboundsql = new boundsql(statement.getconfiguration(), sql, boundsql.getparametermappings(), parameterobject);  
        mappedstatement newstatement = copyfrommappedstatement(statement, new boundsqlsqlsource(newboundsql));  
        invocation.getargs()[0] = newstatement;  
        return invocation.proceed();  
    }  
  
    /**  
     * 判断是否跳过  
     *  
     * @return 是否跳过  
     */  
    private boolean skip() {  
        dataaccesstype[] localaccess = dataaccessmethod.getlocalaccess();  
        return localaccess == null;  
    }  
  
    private string addtenantcondition(string originalsql, string depid, string alias) {  
        string field = "id";  
        if (stringutils.hastext(alias)) {  
            field = alias + "." + field;  
        }  
  
        stringbuilder sb = new stringbuilder(originalsql.tolowercase());  
        int index = sb.indexof("where");  
        sb = new stringbuilder(originalsql);  
        if (index < 0) {  
            sb.append(" where ").append(field).append(" = ").append(depid);  
        } else {  
            sb.insert(index + 5, " " + field + " = " + depid + " and ");  
        }  
        return sb.tostring();  
    }  
  
    private mappedstatement copyfrommappedstatement(mappedstatement ms, sqlsource newsqlsource) {  
        mappedstatement.builder builder = new mappedstatement.builder(ms.getconfiguration(), ms.getid(), newsqlsource, ms.getsqlcommandtype());  
        builder.resource(ms.getresource());  
        builder.fetchsize(ms.getfetchsize());  
        builder.statementtype(ms.getstatementtype());  
        builder.keygenerator(ms.getkeygenerator());  
        builder.timeout(ms.gettimeout());  
        builder.parametermap(ms.getparametermap());  
        builder.resultmaps(ms.getresultmaps());  
        builder.cache(ms.getcache());  
        builder.usecache(ms.isusecache());  
        return builder.build();  
    }  
  
    public static class boundsqlsqlsource implements sqlsource {  
        private final boundsql boundsql;  
  
        public boundsqlsqlsource(boundsql boundsql) {  
            this.boundsql = boundsql;  
        }  
  
        @override  
        public boundsql getboundsql(object parameterobject) {  
            return boundsql;  
        }  
    }  
  
  
}

总结

以上代码只是示例,在实际生产中还需要考虑多表查询、sql注入等相关问题。

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

(0)

相关文章:

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

发表评论

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