Java整合mybatis实现过滤数据

场景

  • 权限1:只能看到自己创建的数据
  • 权限2:只能看到本部门的数据
  • 权限3:查看全部数据

例子

小明有权限1:

{
    "code": "200",
    "msg": "查询成功",
    "data": [
        {
            "name": "name1",
            "id": "1",
            "creater": "xiaoming"
        },
        {
            "name": "name2",
            "id": "2",
            "creater": "xiaoming"
        }
    ]
}

大明具有权限2,小明相同部门

{
    "code": "200",
    "msg": "查询成功",
    "data": [
        {
            "name": "name1",
            "id": "1",
            "creater": "xiaoming"
        },
        {
            "name": "name2",
            "id": "2",
            "creater": "xiaoming"
        },
        {
            "name": "name3",
            "id": "3",
            "creater": "daming"
        },
        {
            "name": "name4",
            "id": "4",
            "creater": "daming"
        }
    ]
}

领导有权限3,可以看到全部

{
    "code": "200",
    "msg": "查询成功",
    "data": [
        {
            "name": "name1",
            "id": "1",
            "creater": "xiaoming"
        },
        {
            "name": "name2",
            "id": "2",
            "creater": "xiaoming"
        },
        {
            "name": "name3",
            "id": "3",
            "creater": "daming"
        },
        {
            "name": "name4",
            "id": "4",
            "creater": "daming"
        },
        {
            "name": "name5",
            "id": "5",
            "creater": "mingming"
        }
    ]
}

执行流程

Java整合mybatis实现过滤数据_第1张图片

  • Configuration:初始化基础配置,比如 MyBatis 的别名等,一些重要的类型对象,如插件,映射器,ObjectFactory 和 TypeHandler对象, MyBatis所有的配置信息都维持在 Configuration 对象之中。
  • SqlSessionFactory:SqlSession工厂。
  • SqlSession:作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要的数据库增删改查功能。
  • Executor:真正执行sql语句的对象,调用 sqlSession 的方法时,本质上都是调用 executor 的方法,还负责获取 connection,创建 StatementHandler ,责调用 StatementHandler 操作数据库 ,并把结果集通过 ResultSetHandler 进行自动映射,另外,它还处理二级缓存的操作。
  • StatementHandler:可以理解为是一次语句的执行,创建并持有 ParameterHandler 和 ResultSetHandler 对象,操作 dbc 的statement与进行数据库操作,另外它也实现了MyBatis的一级缓存。
  • ParameterHandler: 处理入参,将java方法上的参数设置到被执行语句中
  • ResultSetHandler:处理返回结果,处理jdbc的返回值,将其转换为java的对象
  • TypeHandler:负责Java数据类型和JDBC数据类型之间的映射和转换,在 ParameterHandler 和 ResultsetHandler 中
  • MappedStatement:MappedStatement维护了一条 节点的封装。
  • SqlSource:负责根据用户传递的 parameterObject,动态地生成 SQL 语句,将信息封装到 BoundSql 对象中,并返回。
  • BoundSql:表示动态生成的 SQL 语句以及相应的参数信息。

配置切面

  • Interceptor: 实现 Interceptor 接口,mybatis 的拦截器
  • @Intercepts:只有一个属性,即value,其返回值类型是一个@Signature类型的数组,表示我们可以配置多个@Signature注解
  • @Signature:一个方法签名,其共有三个属性,分别为:type、method、args
  • type:拦截的四种类型:Executor - 执行器方法 、StatementHandler - 拦截SQL语法构建处理、ParameterHandler - 拦截参数处理、ResultSetHandler - 拦截结果集处理
  • method:对应接口中的某一个方法名,比如 Executor 的 query 方法
  • args:对应接口中的某一个方法的参数,比如 Executor 中 query 方法因为重载原因,有多个,args就是指明参数类型,从而确定是具体哪一个方法;
@Intercepts({@Signature(type = org.apache.ibatis.executor.Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})@Slf4jpublic class MapperPermissionInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { Object target = invocation.getTarget(); //被代理对象 Method method = invocation.getMethod(); //代理方法 Object[] args = invocation.getArgs(); //方法参数MappedStatement mappedStatement = (MappedStatement) args[0]; Object parameterObject = args[1]; // do something ...方法拦截前执行代码块 Object result = invocation.proceed(); // do something ...方法拦截后执行代码块 return result; } }@Intercepts({@Signature(type = org.apache.ibatis.executor.Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
@Slf4j
public class MapperPermissionInterceptor implements Interceptor {
 @Override
    public Object intercept(Invocation invocation) throws Throwable {
		Object target = invocation.getTarget(); //被代理对象
        Method method = invocation.getMethod(); //代理方法
        Object[] args = invocation.getArgs(); //方法参数
		MappedStatement mappedStatement = (MappedStatement) args[0];
        Object parameterObject = args[1];
        // do something ...方法拦截前执行代码块
        Object result = invocation.proceed();
        // do something ...方法拦截后执行代码块
        return result;
    }
  }

方案

原 originalSql

select id, name, creater from tt

通过 mybatis 的切面实现

在前面添加 select * from ( ,后面添加 where 条件

select * from ( 
	select id, name, creater from tt
) tmp 
where creater = "daming"

实现

  • 配置权限开关
  • 判断方法名是否包含 list 或者 page
  • 添加 where 条件
@Intercepts({@Signature(type = org.apache.ibatis.executor.Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
@Slf4j
public class MapperPermissionInterceptor implements Interceptor {
    @Autowired
    PermissionProperty permissionProperty;
    public static final String PACK_START_SQL = "select * from ( ";
    public static final String PACK_END_SQL = ") tmp ";
    public static final String PERMISSION_Y = "y";
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        if(!PERMISSION_Y.toLowerCase().equals(permissionProperty.getMapper().toLowerCase())){
            log.info("mapper 权限未开启");
            return invocation.proceed();
        }
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        Object parameter = invocation.getArgs()[1];
        BoundSql boundSql = mappedStatement.getBoundSql(parameter);
        String originalSql = boundSql.getSql().trim();
        String id = mappedStatement.getId();
        log.info("originalSql: {} ", originalSql);
        log.info("id: {}", id);
        String name = getMapperName(id);
        String method = getMapperMethod(id);
        log.info("name: {}, method: {}  ", name, method);
        String joinSql = dataPermissionSqlProvider().getJoinSql(name, method);
        log.info("joinSql: {} ", joinSql);
        if (Optional.ofNullable(joinSql).isPresent()) {
            BoundSql newBoundSql = copyFromBoundSql(mappedStatement, boundSql, this.joinSql(originalSql, joinSql));
            ParameterMap map = mappedStatement.getParameterMap();
            MappedStatement newMs = copyFromMappedStatement(mappedStatement, new BoundSqlSqlSource(newBoundSql), map);
            invocation.getArgs()[0] = newMs;
        }
        return invocation.proceed();
    }
    /**
     * 获取 Mapper 名称
     * @param id
     * @return
     */
    private String getMapperName(String id) {
        String name = id.substring(0, id.lastIndexOf("."));
        return name.substring(name.lastIndexOf(".") + 1);
    }
    /**
     * 获取 Mapper 方法
     * @param id
     * @return
     */
    private String getMapperMethod(String id) {
        String method = id.substring(id.lastIndexOf(".") + 1);
        return method;
    }
    private DataPermissionProvider dataPermissionSqlProvider() {
        return (DataPermissionProvider) ApplicationContextUtil.getBean("dataPermissionProvider");
    }
    private String joinSql(String sql, String selectSql) {
        sql = PACK_START_SQL + sql + PACK_END_SQL + selectSql;
        log.info("packSql: {}", sql);
        return sql;
    }
    public static class BoundSqlSqlSource implements SqlSource {
        BoundSql boundSql;
        public BoundSqlSqlSource(BoundSql boundSql) {
            this.boundSql = boundSql;
        }
        @Override
        public BoundSql getBoundSql(Object parameterObject) {
            return boundSql;
        }
    }
    private BoundSql copyFromBoundSql(MappedStatement ms, BoundSql boundSql, String sql) {
        BoundSql newBoundSql = new BoundSql(ms.getConfiguration(), sql, boundSql.getParameterMappings(), boundSql.getParameterObject());
        for (ParameterMapping mapping : boundSql.getParameterMappings()) {
            String prop = mapping.getProperty();
            if (boundSql.hasAdditionalParameter(prop)) {
                newBoundSql.setAdditionalParameter(prop, boundSql.getAdditionalParameter(prop));
            }
        }
        return newBoundSql;
    }
    /**
     * 复制MappedStatement对象
     */
    private MappedStatement copyFromMappedStatement(MappedStatement ms, SqlSource newSqlSource, ParameterMap parameterMap) {
        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(parameterMap);
        builder.resultMaps(ms.getResultMaps());
        builder.resultSetType(ms.getResultSetType());
        builder.cache(ms.getCache());
        builder.flushCacheRequired(ms.isFlushCacheRequired());
        builder.useCache(ms.isUseCache());
        return builder.build();
    }
}

到此这篇关于Java整合mybatis实现过滤数据的文章就介绍到这了,更多相关Java mybatis过滤数据内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

你可能感兴趣的:(Java整合mybatis实现过滤数据)