本文主要是介绍Mybatis拦截器如何实现数据权限过滤,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
《Mybatis拦截器如何实现数据权限过滤》本文介绍了MyBatis拦截器的使用,通过实现Interceptor接口对SQL进行处理,实现数据权限过滤功能,通过在本地线程变量中存储数据权限相关信息,并...
背景
现在的项目负责人去年年底离职,导致前期规划的数据过滤功能一直没有去实现。
现在项目马上进入试运行时期了,需要根据用户数据权限的配置对数据进行过滤处理。
如果一个个手动去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) { http://www.chinasem.cn 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 DataAccessFilterIntpythonerceptor 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 MappedStatemejsnt 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 puandroidblic BoundSql getBoundSql(Object parameterObject) { return boundSql; } } }
总结
以上代码只是示例,在实际生产中还需要考虑多表查询、SQL注入等相关问题。
这篇关于Mybatis拦截器如何实现数据权限过滤的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!