背景
现在的项目负责人去年年底离职,导致前期规划的数据过滤功能一直没有去实现。
现在项目马上进入试运行时期了,需要根据用户数据权限的配置对数据进行过滤处理。
如果一个个手动去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注入等相关问题。
这些仅为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论