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