mybatis数据权限

贴上自己的码云地址

https://gitee.com/imbobo_bo/angel-bo 下面有不懂得可以直接下载源码,进行一次深入了解

需求场景

在做项目几年经常设计各种权限,比如资源权限,还有就是数据权限了,要实现可配置的

原理

这几天闲来无事,在研究mybatis的分页和拦截器,想到了用拦截器来实现数据权限的问题,用拦截器拦截sql,然后拼装sql完成数据权限的读取

实践

第一次实践

既然要用到mybatis拦截器,顺手拿来一个写好的mybatis拦截器修改

@Intercepts(value = {
        @Signature (type=Executor.class,  method="update", args={MappedStatement.class,Object.class}),
        @Signature(type=Executor.class, method="query", args={MappedStatement.class,Object.class,RowBounds.class,ResultHandler.class, CacheKey.class,BoundSql.class}),
        @Signature(type=Executor.class,  method="query",  args={MappedStatement.class,Object.class,RowBounds.class,ResultHandler.class})})
public class SQLMonitorPlugin implements Interceptor {

    private static final Logger LOGGER = LoggerFactory.getLogger(SQLMonitorPlugin.class);
    //是否监控显示SQL
    private static final boolean SHOWSQL = true;
    //慢SQL时间阀值,单位毫秒
    private static final int SLOWER = 3000;
    //大集合监控阀值,单位条
    private static final int MAXCOUNT = 80;

    public SQLMonitorPlugin() {
    }

    @Override
    @SuppressWarnings("ALL")
    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        //请求参数
        Object parameter = null;
        if (1 < invocation.getArgs().length) {
            parameter = invocation.getArgs()[1];
        }
        BoundSql boundSql = mappedStatement.getBoundSql(parameter);
         String sql = boundSql.getSql();
        //执行的sql所在的mapper文件
        String resource = mappedStatement.getResource();
        //执行sql的dao文件的包名+方法名
        String daoMethod = mappedStatement.getId();
        //去除sql文中的换行
        sql = sql.replace("\n", "").replaceAll("\\s+", " ");

        //sql语句类型 select、delete、insert、update
        String sqlCommandType = mappedStatement.getSqlCommandType().toString();

        if (SHOWSQL) {
            LOGGER.warn("[SQLMonitorPlugin]SQL监控:{}|{},执行SQL:{},参数:{}", resource, daoMethod,
                    sql, JsonSerializeUtil.toJson(parameter));
        }
        try {

            //这里处理SQL 替换SQL



            long start = System.currentTimeMillis();
            Object e = invocation.proceed();
            long timeTicket = System.currentTimeMillis() - start;
            if (SLOWER < timeTicket) {
                LOGGER.warn("[SQLMonitorInterceptor]SQL监控:{}|{},慢SQL({}/{}ms){},参数:{}",
                        resource, daoMethod, timeTicket, SLOWER, sql, JsonSerializeUtil.toJson(parameter));
            }
            if (e instanceof Collection) {
                int ct = ((Collection<?>) e).size();
                if (MAXCOUNT < ct) {
                    LOGGER.warn("[SQLMonitorInterceptor]SQL监控:{}|{},大集合({}/{}){},参数:{}",
                            resource, daoMethod, ct, MAXCOUNT, sql, JsonSerializeUtil.toJson(parameter));
                }
            }
            return e;
        } catch (Exception e) {
            LOGGER.warn("[SQLMonitorPlugin]SQL监控:{}|{},执行出错:{},参数:{}", resource, daoMethod,
                    sql, JsonSerializeUtil.toJson(parameter), e);
            throw e;
        }
    }

。。。省略了

}

想着可以在这个记录SQL的拦截器里直接修改SQL,很可惜,失败了,因为拦截器有几种具体要拦截的阶段,向上边的代码如果修改了SQL然而执行的还是原SQL,所以达不到想要的结果。 思考之后,决定再另起一个拦截器

第二次尝试

这次选择了拦截器

 @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})

这个的意思是拦截SQL构建过程,在执行之前 摘一段码云上的代码,代码并不完整,只体会题意

@Intercepts({
        @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class,Integer.class })
})
@Component
public class PrepareInterceptor implements Interceptor {
    /** 日志 */
    private static final Logger log = LoggerFactory.getLogger(PrepareInterceptor.class);

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

    @Override
    public void setProperties(Properties properties) {}

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        if(log.isInfoEnabled()){
            log.info("进入 PrepareInterceptor 拦截器...");
        }
        if(invocation.getTarget() instanceof RoutingStatementHandler) {
            RoutingStatementHandler handler = (RoutingStatementHandler) invocation.getTarget();
            StatementHandler delegate = (StatementHandler) ReflectUtil.getFieldValue(handler, "delegate");
            //通过反射获取delegate父类BaseStatementHandler的mappedStatement属性
            MappedStatement mappedStatement = (MappedStatement) ReflectUtil.getFieldValue(delegate, "mappedStatement");
            //千万不能用下面注释的这个方法,会造成对象丢失,以致转换失败
            //MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
            PermissionAop permissionAop = PermissionUtils.getPermissionByDelegate(mappedStatement);
            if(permissionAop == null){
                if(log.isInfoEnabled()){
                    log.info("数据权限放行...");
                }
                return invocation.proceed();
            }
            if(log.isInfoEnabled()){
                log.info("数据权限处理【拼接SQL】...");
            }
            BoundSql boundSql = delegate.getBoundSql();
            ReflectUtil.setFieldValue(boundSql, "sql", permissionSql(boundSql.getSql()));
        }
        return invocation.proceed();
    }

    /**
     * 权限sql包装
     * @author GaoYuan
     * @date 2018/4/17 上午9:51
     */
    protected String permissionSql(String sql) {
        StringBuilder sbSql = new StringBuilder(sql);
        String userMethodPath = PermissionConfig.getConfig("permission.client.userid.method");
        //当前登录人
        String userId = (String)ReflectUtil.reflectByPath(userMethodPath);
        //如果用户为 1 则只能查询第一条
        if("1".equals(userId)){
            //sbSql = sbSql.append(" limit 1 ");
            //如果有动态参数 regionCd
            if(true){
                String premission_param = "regionCd";
                //select * from (select id,name,region_cd from sys_exam ) where region_cd like '${}%'
                String methodPath = PermissionConfig.getConfig("permission.client.params." + premission_param);
                String regionCd = (String)ReflectUtil.reflectByPath(methodPath);
                sbSql = new StringBuilder("select * from (").append(sbSql).append(" ) s where s.regionCd like concat("+ regionCd +",'%')  ");
            }

        }
        return sbSql.toString();
    }
}

运行结果是:SQL确实拼装完成,加入了权限控制的代码段,但是再运行时与mybatis分页插件起了冲突

SELECT count(0)
FROM base_user u LEFT JOIN base_organization o ON u.organization_id = o.organization_id
  LEFT JOIN (SELECT
               GROUP_CONCAT(r.role_name) AS role_name,
               ur.user_id
             FROM base_user_role ur LEFT JOIN base_role r ON r.role_id = ur.role_id
            ) roleT ON roleT.user_id = u.user_id
WHERE 1 = 1

拼接成了

SELECT b.*
FROM (SELECT count(0)
      FROM base_user u LEFT JOIN base_organization o ON u.organization_id = o.organization_id
        LEFT JOIN (SELECT
                     GROUP_CONCAT(r.role_name) AS role_name,
                     ur.user_id
                   FROM base_user_role ur LEFT JOIN base_role r ON r.role_id = ur.role_id
                   GROUP BY ur.user_id) roleT ON roleT.user_id = u.user_id
      WHERE 1 = 1) AS b
WHERE b.create_user_id = 1

SQL执行报错了,分析原因,肯定是分页的拦截器优先于权限拦截器了 这时候去看分页的源码了 我这里用的pagehelper 5.0.4 码云上有的

**
 * Mybatis - 通用分页拦截器<br/>
 * 项目地址 : http://git.oschina.net/free/Mybatis_PageHelper
 *
 * @author liuzh/abel533/isea533
 * @version 5.0.0
 */
@SuppressWarnings({"rawtypes", "unchecked"})
@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 PageInterceptor implements Interceptor {
    //缓存count查询的ms
    protected Cache<String, MappedStatement> msCountMap = null;
    private Dialect dialect;
    private String default_dialect_class = "com.github.pagehelper.PageHelper";
    private Field additionalParametersField;
    private String countSuffix = "_COUNT";

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        try {
            Object[] args = invocation.getArgs();
            MappedStatement ms = (MappedStatement) args[0];
            Object parameter = args[1];
            RowBounds rowBounds = (RowBounds) args[2];

原来 Executor拦截器优先于 StatementHandler ,由于第一次尝试的时候用了 Executor 所以貌似进入了死胡同

第三次尝试

有没有办法在执行分页的时候或者之前完成拼装SQL呢

if (!dialect.skip(ms, parameter, rowBounds)) {
                //反射获取动态参数
                String msId = ms.getId();
                Configuration configuration = ms.getConfiguration();
                Map<String, Object> additionalParameters = (Map<String, Object>) additionalParametersField.get(boundSql);
                //判断是否需要进行 count 查询
                if (dialect.beforeCount(ms, parameter, rowBounds)) {
                    String countMsId = msId + countSuffix;
                    Long count;
                    //先判断是否存在手写的 count 查询
                    MappedStatement countMs = getExistedMappedStatement(configuration, countMsId);
                    if(countMs != null){
                        count = executeManualCount(executor, countMs, parameter, boundSql, resultHandler);
                    } else {
                        countMs = msCountMap.get(countMsId);
                        //自动创建
                        if (countMs == null) {
                            //根据当前的 ms 创建一个返回值为 Long 类型的 ms
                            countMs = MSUtils.newCountMappedStatement(ms, countMsId);
                            msCountMap.put(countMsId, countMs);
                        }
                        count = executeAutoCount(executor, countMs, parameter, boundSql, rowBounds, resultHandler);
                    }
                    //处理查询总数
                    //返回 true 时继续分页查询,false 时直接返回
                    if (!dialect.afterCount(count, parameter, rowBounds)) {
                        //当查询总数为 0 时,直接返回空的结果
                        return dialect.afterPage(new ArrayList(), parameter, rowBounds);
                    }

找到pagehelper拦截器执行count方法executeAutoCount

private Long executeAutoCount(Executor executor, MappedStatement countMs,
                                   Object parameter, BoundSql boundSql,
                                   RowBounds rowBounds, ResultHandler resultHandler) throws IllegalAccessException, SQLException {
        Map<String, Object> additionalParameters = (Map<String, Object>) additionalParametersField.get(boundSql);
        //创建 count 查询的缓存 key
        CacheKey countKey = executor.createCacheKey(countMs, parameter, RowBounds.DEFAULT, boundSql);
        //调用方言获取 count sql
        String countSql = dialect.getCountSql(countMs, boundSql, parameter, rowBounds, countKey);
        //countKey.update(countSql);
        BoundSql countBoundSql = new BoundSql(countMs.getConfiguration(), countSql, boundSql.getParameterMappings(), parameter);
        //当使用动态 SQL 时,可能会产生临时的参数,这些参数需要手动设置到新的 BoundSql 中
        for (String key : additionalParameters.keySet()) {
            countBoundSql.setAdditionalParameter(key, additionalParameters.get(key));
        }
        //执行 count 查询
        Object countResultList = executor.query(countMs, parameter, RowBounds.DEFAULT, resultHandler, countKey, countBoundSql);
        Long count = (Long) ((List) countResultList).get(0);
        return count;
    }

主要是

String countSql = dialect.getCountSql(countMs, boundSql, parameter, rowBounds, countKey);

再看 dialect是啥

 @Override
    public void setProperties(Properties properties) {
        //缓存 count ms
        msCountMap = CacheFactory.createCache(properties.getProperty("msCountCache"), "ms", properties);
        String dialectClass = properties.getProperty("dialect");
        if (StringUtil.isEmpty(dialectClass)) {
            dialectClass = default_dialect_class;
        }
        try {
            Class<?> aClass = Class.forName(dialectClass);
            dialect = (Dialect) aClass.newInstance();
        } catch (Exception e) {
            throw new PageException(e);
        }
        dialect.setProperties(properties);

        String countSuffix = properties.getProperty("countSuffix");
        if (StringUtil.isNotEmpty(countSuffix)) {
            this.countSuffix = countSuffix;
        }

        try {
            //反射获取 BoundSql 中的 additionalParameters 属性
            additionalParametersField = BoundSql.class.getDeclaredField("additionalParameters");
            additionalParametersField.setAccessible(true);
        } catch (NoSuchFieldException e) {
            throw new PageException(e);
        }
    }

原来是初始化的时候注入的 默认使用的是


package com.github.pagehelper;

import com.github.pagehelper.dialect.AbstractHelperDialect;
import com.github.pagehelper.page.PageAutoDialect;
import com.github.pagehelper.page.PageMethod;
import com.github.pagehelper.page.PageParams;
import com.github.pagehelper.util.MSUtils;
import com.github.pagehelper.util.StringUtil;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.RowBounds;

import java.util.List;
import java.util.Properties;

/**
 * Mybatis - 通用分页拦截器<br/>
 * 项目地址 : http://git.oschina.net/free/Mybatis_PageHelper
 *
 * @author liuzh/abel533/isea533
 * @version 5.0.0
 */
public class PageHelper extends PageMethod implements Dialect {
    private PageParams pageParams;
    private PageAutoDialect autoDialect;

    @Override
    public String getPageSql(MappedStatement ms, BoundSql boundSql, Object parameterObject, RowBounds rowBounds, CacheKey pageKey) {
        return autoDialect.getDelegate().getPageSql(ms, boundSql, parameterObject, rowBounds, pageKey);
    }

    public String getPageSql(String sql, Page page, RowBounds rowBounds, CacheKey pageKey) {
        return autoDialect.getDelegate().getPageSql(sql, page, pageKey);
    }

那autoDialect是啥呢 在拦截器注入的时候调用了这个方法

    @Override
    public void setProperties(Properties properties) {
        pageParams = new PageParams();
        autoDialect = new PageAutoDialect();
        pageParams.setProperties(properties);
        autoDialect.setProperties(properties);
    }

继续深入

    public void setProperties(Properties properties) {
        //多数据源时,获取 jdbcurl 后是否关闭数据源
        String closeConn = properties.getProperty("closeConn");
        if (StringUtil.isNotEmpty(closeConn)) {
            this.closeConn = Boolean.parseBoolean(closeConn);
        }
        //指定的 Helper 数据库方言,和  不同
        String dialect = properties.getProperty("helperDialect");
        //运行时获取数据源
        String runtimeDialect = properties.getProperty("autoRuntimeDialect");
        //1.动态多数据源
        if (StringUtil.isNotEmpty(runtimeDialect) && runtimeDialect.equalsIgnoreCase("TRUE")) {
            this.autoDialect = false;
            this.properties = properties;
        }
        //2.动态获取方言
        else if (StringUtil.isEmpty(dialect)) {
            autoDialect = true;
            this.properties = properties;
        }
        //3.指定方言
        else {
            autoDialect = false;
            this.delegate = initDialect(dialect, properties);
        }
    }

看下initDialect这个方法


    /**
     * 初始化 helper
     *
     * @param dialectClass
     * @param properties
     */
    private AbstractHelperDialect initDialect(String dialectClass, Properties properties) {
        AbstractHelperDialect dialect;
        if (StringUtil.isEmpty(dialectClass)) {
            throw new PageException("使用 PageHelper 分页插件时,必须设置 helper 属性");
        }
        try {
            Class sqlDialectClass = resloveDialectClass(dialectClass);
            if (AbstractHelperDialect.class.isAssignableFrom(sqlDialectClass)) {
                dialect = (AbstractHelperDialect) sqlDialectClass.newInstance();
            } else {
                throw new PageException("使用 PageHelper 时,方言必须是实现 " + AbstractHelperDialect.class.getCanonicalName() + " 接口的实现类!");
            }
        } catch (Exception e) {
            throw new PageException("初始化 helper [" + dialectClass + "]时出错:" + e.getMessage(), e);
        }
        dialect.setProperties(properties);
        return dialect;
    }
    /**
     * 反射类
     *
     * @param className
     * @return
     * @throws Exception
     */
    private Class resloveDialectClass(String className) throws Exception {
        if (dialectAliasMap.containsKey(className.toLowerCase())) {
            return dialectAliasMap.get(className.toLowerCase());
        } else {
            return Class.forName(className);
        }
    }

原来这里是获取数据库方言,然后分页时调用数据库方言的实现类,完成分页SQL拼装的,所以接下来就是看下这个方言了,从上面代码可以看到方言是可以加载指定类的

return Class.forName(className);

    static {
        //注册别名
        dialectAliasMap.put("hsqldb", HsqldbDialect.class);
        dialectAliasMap.put("h2", HsqldbDialect.class);
        dialectAliasMap.put("postgresql", HsqldbDialect.class);
        dialectAliasMap.put("phoenix", HsqldbDialect.class);

        dialectAliasMap.put("mysql", MySqlDialect.class);
        dialectAliasMap.put("mariadb", MySqlDialect.class);
        dialectAliasMap.put("sqlite", MySqlDialect.class);

        dialectAliasMap.put("oracle", OracleDialect.class);
        dialectAliasMap.put("db2", Db2Dialect.class);
        dialectAliasMap.put("informix", InformixDialect.class);

        dialectAliasMap.put("sqlserver", SqlServerDialect.class);
        dialectAliasMap.put("sqlserver2012", SqlServer2012Dialect.class);

        dialectAliasMap.put("derby", SqlServer2012Dialect.class);
    }
public class MySqlDialect extends AbstractHelperDialect {

    @Override
    public String getPageSql(String sql, Page page, CacheKey pageKey) {
        StringBuilder sqlBuilder = new StringBuilder(sql.length() + 14);
        sqlBuilder.append(sql);
        if (page.getStartRow() == 0) {
            sqlBuilder.append(" LIMIT ");
            sqlBuilder.append(page.getPageSize());
        } else {
            sqlBuilder.append(" LIMIT ");
            sqlBuilder.append(page.getStartRow());
            sqlBuilder.append(",");
            sqlBuilder.append(page.getPageSize());
            pageKey.update(page.getStartRow());
        }
        pageKey.update(page.getPageSize());
        return sqlBuilder.toString();
    }

}

最终发现分页调用的SQL为

/**
 * 针对 PageHelper 的实现
 *
 * @author liuzh
 * @since 2016-12-04 14:32
 */
public abstract class AbstractHelperDialect extends AbstractDialect {

    /**
     * 获取分页参数
     *
     * @param <T>
     * @return
     */
    public <T> Page<T> getLocalPage() {
        return PageHelper.getLocalPage();
    }

    @Override
    public final boolean skip(MappedStatement ms, Object parameterObject, RowBounds rowBounds) {
        //该方法不会被调用
        return true;
    }

    @Override
    public boolean beforeCount(MappedStatement ms, Object parameterObject, RowBounds rowBounds) {
        Page page = getLocalPage();
        return page.isCount();
    }

    @Override
    public String getCountSql(MappedStatement ms, BoundSql boundSql, Object parameterObject, RowBounds rowBounds, CacheKey countKey) {
        Page<Object> page = getLocalPage();
        String countColumn = page.getCountColumn();
        if (StringUtil.isNotEmpty(countColumn)) {
            return countSqlParser.getSmartCountSql(boundSql.getSql(), countColumn);
        }
        return countSqlParser.getSmartCountSql(boundSql.getSql());
    }

    @Override
    public boolean afterCount(long count, Object parameterObject, RowBounds rowBounds) {
        Page page = getLocalPage();
        page.setTotal(count);
        if (rowBounds instanceof PageRowBounds) {
            ((PageRowBounds) rowBounds).setTotal(count);
        }
        //pageSize < 0 的时候,不执行分页查询
        //pageSize = 0 的时候,还需要执行后续查询,但是不会分页
        if (page.getPageSize() < 0) {
            return false;
        }
        return count > 0;
    }

    @Override
    public Object processParameterObject(MappedStatement ms, Object parameterObject, BoundSql boundSql, CacheKey pageKey) {
        return parameterObject;
    }

    @Override
    public boolean beforePage(MappedStatement ms, Object parameterObject, RowBounds rowBounds) {
        Page page = getLocalPage();
        if (page.getPageSize() > 0) {
            return true;
        }
        return false;
    }

    @Override
    public String getPageSql(MappedStatement ms, BoundSql boundSql, Object parameterObject, RowBounds rowBounds, CacheKey pageKey) {
        String sql = boundSql.getSql();
        Page page = getLocalPage();
        return getPageSql(sql, page, pageKey);
    }

    /**
     * 单独处理分页部分
     *
     * @param sql
     * @param page
     * @param pageKey
     * @return
     */
    public abstract String getPageSql(String sql, Page page, CacheKey pageKey);

    @Override
    public Object afterPage(List pageList, Object parameterObject, RowBounds rowBounds) {
        Page page = getLocalPage();
        if (page == null) {
            return pageList;
        }
        page.addAll(pageList);
        if (!page.isCount()) {
            page.setTotal(-1);
        } else if ((page.getPageSizeZero() != null && page.getPageSizeZero()) && page.getPageSize() == 0) {
            page.setTotal(pageList.size());
        }
        return page;
    }

    @Override
    public void afterAll() {

    }

    @Override
    public void setProperties(Properties properties) {

    }
}

这就好办了,重写这个,然后让功能加载这个自定义方言就可以了

@Slf4j
public abstract class PermissionDialect extends AbstractHelperDialect{
    /**
     * 单独处理分页部分
     *
     * @param sql
     * @param page
     * @param pageKey
     * @return
     */
    @Override
    public String getPageSql(String sql, Page page, CacheKey pageKey) {
        StringBuilder sqlBuilder = new StringBuilder(sql.length() + 14);
        sqlBuilder.append(sql);
        if (page.getStartRow() == 0) {
            sqlBuilder.append(" LIMIT ");
            sqlBuilder.append(page.getPageSize());
        } else {
            sqlBuilder.append(" LIMIT ");
            sqlBuilder.append(page.getStartRow());
            sqlBuilder.append(",");
            sqlBuilder.append(page.getPageSize());
            pageKey.update(page.getStartRow());
        }
        pageKey.update(page.getPageSize());
        return sqlBuilder.toString();
    }

    @Override
    public String getCountSql(MappedStatement ms, BoundSql boundSql, Object parameterObject, RowBounds rowBounds, CacheKey countKey) {
        ReflectUtil.setFieldValue(boundSql, "sql", permissionSql(boundSql.getSql()));
        return super.getCountSql(ms, boundSql, parameterObject, rowBounds, countKey);
    }


    @Override
    public String getPageSql(MappedStatement ms, BoundSql boundSql, Object parameterObject, RowBounds rowBounds, CacheKey pageKey) {
        return super.getPageSql(ms, boundSql, parameterObject, rowBounds, pageKey);
    }


    public abstract String permissionSql(String sql);


}

在需要实现的地方,集成这个类,并实现permissionSql方法,然后指定加载这个就可以了


/**
 * 数据权限处理SQL
 */
@Slf4j
public class SQLPermissionDialect extends PermissionDialect {


    @Override
    public String permissionSql(String sql) {
        PermissionService permissionService = getBean(PermissionService.class);
        WebReturn webReturn ;

        AuthUtil authUtil = getBean(AuthUtil.class);
        UserRole userRole = new UserRole();
        userRole.setUserId(authUtil.getUserIdFromToken());

        webReturn = permissionService.getThePermissionDataByCurrentUser(userRole);

        if(webReturn != null && "".equals(webReturn.getData())){
            return sql;
        }

        StringBuilder sbSql = new StringBuilder(sql);
        sbSql = new StringBuilder("select permissionDataTable.* from (").append(sbSql).append(" ) as permissionDataTable ");
        if(webReturn != null && webReturn.getData() != null){

            sbSql.append(webReturn.getData());
        }
        log.info("权限拼装SQL:{}", sbSql.toString());
        return sbSql.toString();

    }
}
pagehelper:
  helper-dialect: com.angel.bo.admin.plugin.SQLPermissionDialect
  reasonable: true
  support-methods-arguments: true

转载于:https://my.oschina.net/angelbo/blog/2874181

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Spring Boot中使用MyBatis实现数据权限控制的方法可以通过自定义拦截器来实现。首先,你可以创建一个自定义拦截器,比如命名为PowerInterceptor。这个拦截器需要实现Interceptor接口,并且使用@Intercepts注解来指定拦截的方法和参数。在这个拦截器中,你可以通过重写intercept方法来实现对SQL语句的处理。 在intercept方法中,你可以获取到方法的调用信息,并且可以对SQL进行修改或者添加额外的条件。比如,你可以在该方法中获取到StatementHandler对象,并通过调用getBoundSql方法来获取到SQL语句。然后,你可以根据自定义的数据权限规则来修改SQL语句,添加相应的条件。最后,通过调用invocation.proceed()方法来继续执行下一个拦截器或者目标方法。 为了将自定义拦截器应用到MyBatis中,你需要在拦截器中实现Plugin接口,并将其配置为一个Bean。在plugin方法中,你可以判断被代理对象是否是StatementHandler类型的,如果是的话,就使用Plugin.wrap方法对其进行包装,并将自定义拦截器传入。最后,你需要将该拦截器配置到MyBatis的配置文件中或者使用@MapperScan注解进行扫描。 这样,当MyBatis执行SQL语句时,自定义拦截器将会被触发,并对SQL进行相应的处理,从而实现数据权限控制。通过这种方式,你不需要在每个SQL语句中添加权限判断条件,避免了代码冗余和维护困难的问题。 希望以上信息对你有帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值