mybatis實現數據行級權限攔截


最近在做一個測試平台,其中有一個需求是用戶只能看到他有權限的項目數據。一開始這個需求只針對用例模塊,我直接在sql后面加上了關聯項目權限表。后面因為其他模塊也需要這個權限判斷,故打算把關聯sql抽取出來,實現攔截sql並添加權限。

大概分為三步:

1、定義一個注解 PermissionAop (可以在注解中添加參數,實現不同參數做不同的sql拼接)

2、實現攔截器(參考pagehelper的PageInterceptor實現),攔截Executor,當遇到mapper方法帶有注解A,把權限的sql拼接上去

3、添加攔截器,可以使用@Configuration或實現ApplicationListener(自行百度),先添加pagehelper攔截器,再添加自定義的數據權限攔截器

下面說下要注意的點:

1、注意pom文件引入的是pagehelper-spring-boot-starter還是pagehelper。引入pagehelper-spring-boot-starter會自動添加pagehelper攔截器,而引入pagehelper則要手動添加攔截器。(我使用的pagehelper,下面偽代碼有。若使用pagehelper-spring-boot-starter,參考https://blog.csdn.net/weixin_45497155/article/details/106025321)

2、測試時發現生成的sql總是先limit再權限過濾,pagehelper的攔截一直優先於自定義的攔截(這樣會出現limit出10條,權限過濾剩5條,實際要求是先權限過濾,再limit出10條),一開始以為是添加攔截器的順序問題,后面在官方文檔看到,是攔截的對象不對(百度很多文章寫的都是攔截StatementHandler),應該要攔截Executor

詳細參考pagehelper官方文檔:https://gitee.com/free/Mybatis_PageHelper/blob/master/wikis/zh/Interceptor.md

偽代碼:

注解:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @Author: Hjy
 * @Date: 2021/8/18 15:38 (日期和時間)
 * @description 自定義權限Aop注解
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface PermissionAop {
    PermissionEnum type() default PermissionEnum.DEFAULT;
}

  攔截器:

/**
 * @Author: Hjy
 * @Date: 2021/11/4 15:01 (日期和時間)
 * @description sql權限攔截(不能攔截StatementHandler , pageHelper攔截的是Executor
 *具體參考 : https://gitee.com/free/Mybatis_PageHelper/blob/master/wikis/zh/Interceptor.md )
 */
@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}
)})
public class SqlAuthenticationInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        // 這一段復制pageHelper的PageInterceptor
        Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement) args[0];
        Object parameter = args[1];
        RowBounds rowBounds = (RowBounds) args[2];
        ResultHandler resultHandler = (ResultHandler) args[3];
        Executor executor = (Executor) invocation.getTarget();
        CacheKey cacheKey;
        BoundSql boundSql;
        if (args.length == 4) {
            boundSql = ms.getBoundSql(parameter);
            cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);
        } else {
            cacheKey = (CacheKey) args[4];
            boundSql = (BoundSql) args[5];
        }

        // 獲取全類名和方法名
        String id = ms.getId();
        String className = id.substring(0, id.lastIndexOf("."));
        String methodName = id.substring(id.lastIndexOf(".") + 1);
        final Class<?> cls = Class.forName(className);
        final Method[] methods = cls.getMethods();
        for (Method method : methods) {
            // 若該方法含有PermissionAop注解,添加權限sql
            if (method.getName().equals(methodName) && method.isAnnotationPresent(PermissionAop.class)) {
                String permissionSql = getPermissionSql(boundSql.getSql());
                ReflectUtil.setFieldValue(boundSql, "sql", permissionSql);
            }
        }
        return executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
    }

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

    @Override
    public void setProperties(Properties properties) {

    }

    private String getPermissionSql(String boundSql) {
        // 這里拼接你的權限sql
        return boundSql+"";
    }
}

 配置攔截器

/**
 * @Author: Hjy
 * @Date: 2021/11/10 15:39 (日期和時間)
 * @description 自定義mybatis的攔截,不使用pagehelper-spring-boot-starter,目的是添加攔截數據權限
 */
@Configuration
public class MybatisInterceptorAutoConfiguration {

    @Resource
    private List<SqlSessionFactory> sqlSessionFactoryList;

    @Bean
    @ConfigurationProperties(prefix = "pagehelper")
    public Properties pageHelperProperties() {
        return new Properties();
    }

    @PostConstruct
    public void addMysqlInterceptor() {
        //數據權限攔截器
        SqlAuthenticationInterceptor sqlAuthenticationInterceptor = new SqlAuthenticationInterceptor();
        //分頁攔截器
        PageInterceptor pageInterceptor = new PageInterceptor();
        pageInterceptor.setProperties(this.pageHelperProperties());

        // 先增加的攔截后執行
        for (SqlSessionFactory sqlSessionFactory : sqlSessionFactoryList) {
            sqlSessionFactory.getConfiguration().addInterceptor(pageInterceptor);
            // 增加權限攔截
            sqlSessionFactory.getConfiguration().addInterceptor(sqlAuthenticationInterceptor);
        }
    }
}

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM