怎么通过MyBatis自定义插件实现简易数据权限

这篇文章主要讲解了“怎么通过MyBatis自定义插件实现简易数据权限”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么通过MyBatis自定义插件实现简易数据权限”吧!

创新互联是一家集网站建设,沙坡头企业网站建设,沙坡头品牌网站建设,网站定制,沙坡头网站建设报价,网络营销,网络优化,沙坡头网站推广为一体的创新建站企业,帮助传统企业提升企业形象加强企业竞争力。可充分满足这一群体相比中小企业更为丰富、高端、多元的互联网需求。同时我们时刻保持专业、时尚、前沿,时刻以成就客户成长自我,坚持不断学习、思考、沉淀、净化自己,让我们为更多的企业打造出实用型网站。

1.mybatis自定义插件

一说到mybatis插件,都会想到mybatis的分页插件。的确现在开发中用mybatis的话,一般都会用到它。它能在我们的sql语句后面添加分页查询条件,达到分页查询的效果。

①配置

由于mybatis是基于xml插件配置的所以,我们要在xml中配置自己的插件


     //自定义插件的全类名
②接口的拦截

先看看一张图 ,mybatis主要提供了下面4个接口支持拦截 怎么通过MyBatis自定义插件实现简易数据权限
编写DataScopeInterceptor(需要实现Interceptor接口)

@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class DataScopeInterceptor implements Interceptor {
    // 这里是每次执行操作的时候,都会进行这个拦截器的方法内
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        //TODO:自已的业务处理
        return invocation.proceed();
    }
    // 主要是为了把这个拦截器生成一个代理放到拦截器链中
    @Override
    public Object plugin(Object o) {

        return Plugin.wrap(o, this);
    }

    // 插件初始化的时候调用,也只调用一次,插件配置的属性从这里设置进来
    @Override
    public void setProperties(Properties properties) {

    }
}

看看这个注解

@Signature(
    type = StatementHandler.class, //这是指拦截哪个接口
    method = "prepare", //这个是拦截接口中的什么方法,可以在StatementHandler中找到此方法
    args = {Connection.class, Integer.class}//拦截方法的参数列表)

下面我们启动项目,执行sql查询的时候会进入到我们的插件中。现在算是自己定义好了一个空的插件了。

ps:mybatis可以同时定义多个插件,这些插件采用责任链模式,通过代理对象一个一个调用下一个插件,进行执行。

2.数据权限(仅以查询为例)

项目开发中,大多项目都有权限相关的考虑,说到权限,大体分为两类,数据权限与功能权限。数据权限一般是针对不同的角色或者用户,查询到不同的数据,对同一数据表或者数据源的不同条件筛选。而功能权限更多是对功能菜单的访问权限。在不同项目中,权限设计也不同,跟系统业务,架构设计息息相关,做好权限,是非常困难的。我这里仅仅是对数据权限的一个模拟。不涉及具体实现。(这块本来就没有同一的处理方案)。

①授权的分类

我这里将数据权限分为两个类别,明细授权,条件授权
明细授权:对一个表数据,通过id进行授权,比如有个商品表,可以把指定商品的id授权给指定用户或角色,达到该用户或角色可以查询到这一个指定商品。

select * from product where id = 1

条件授权:可以通过指定范围查询条件为某一用户或角色进行授权

select * from product where name like '%iphone%'

上面的两个sql很明显的表现了这两种授权方式的区别与联系。我们不免会想,明细授权也可以用条件授权来实现,达到相同的效果,的确是这样,我们这里仅仅说明两天不同的授权方式,其实在真实的环境中,可能还会有其他的授权形式。

上面我们通过对mybatis插件的自定义,我们就可以对sql进行处理了。所以,数据权限无非就是在自定义插件中获取到sql。在sql语句后面拼接不同的过滤条件来达到数据过滤。真实业务场景更为复杂。很多时候,不仅仅是拼接这么简单。对sql的处理也是一件废功夫的事情。下面看具体实现

②不同授权类型实现

数据权限接口,这里只写了一个getsql的方法定义,参数为原始sql,返回值为经过处理后的sql

public interface DataScopeInterface {

    /**
     * 获取sql
     * @return
     */
    String getSql(String sql);

}

DataScopeInterface有两个不同的实现类,明细与条件

@Component("grantDataScope")
@Slf4j
public class GrantDataScope implements DataScopeInterface {
    //明细授权实现
    @Override
    public String getSql(String sql) {
        //这里是获取当前登录用户的id,我定义了一个登录拦截器,通过ThreadLocal保存当前登录的用户。代码就不写出来了。比较简单
        String userId = LoginHandlerInterceptor.userLoginThreadLocal.get();
        //这里模拟用户id为1的用户在查询语句后面拼接id为1的过滤条件
        if("1".equals(userId)){
            if(sql.toLowerCase().contains("where")){
                sql += ">

条件授权

@Component("ruleDataScope")
@Slf4j
public class RuleDataScope implements DataScopeInterface {
    // 条件授权实现
    public String getSql(String sql) {
        String userId = LoginHandlerInterceptor.userLoginThreadLocal.get();
        //模拟用户为id为2的用户加name中有ipone的数据
        if("2".equals(userId)){
            if(sql.toLowerCase().contains("where")){
                sql += " and name like '%iphone%'";
            }else {
                sql += " where name like '%iphone%'";
            }
        }
        log.info("rule: "+ sql);
        return sql;
    }
}
③插件逻辑实现

下面在自定义插件中编写业务具体实现逻辑。由于我们不同的授权类型都是交给spring管理的bean,我们可以借此通过策略模式来实现授权规则的可扩展性。有不同的授权规则只需要实现DataScopeInterface接口即可。

public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);
        String userName = LoginHandlerInterceptor.userLoginThreadLocal.get();
        //  sql语句类型
        Object sqlCommandType = metaStatementHandler.getValue("delegate.mappedStatement.sqlCommandType");
        //只考虑了查询
        if (SqlCommandType.SELECT.equals(sqlCommandType)) {
            //获取sql
            String sql = String.valueOf(metaStatementHandler.getValue("delegate.boundSql.sql"));
            //获取DataScopeInterface的实现类集合,并循环执行sql的格式化
            Map dataScopeInterfaceMap = SpringContextUtils.getBeanOfType(DataScopeInterface.class);
            Collection dataScopeInterfaces = dataScopeInterfaceMap.values();
            for (DataScopeInterface dataScopeInterface : dataScopeInterfaces) {
                sql = dataScopeInterface.getSql(sql);
            }
            log.info("sql --> " + sql);
            //重新设置sql
            metaStatementHandler.setValue("delegate.boundSql.sql", sql);
        }
        return invocation.proceed();
}
⑤验证

启动项目,模拟用户id为1的用户登录,返回了一条记录,达到了过滤的效果。

2019-08-24 14:07:05,496 DEBUG (BaseJdbcLogger.java:145)- ==>  Preparing: select * from product where id = 1 
2019-08-24 14:07:05,496 DEBUG (BaseJdbcLogger.java:145)- ==> Parameters: 
2019-08-24 14:07:05,498 DEBUG (BaseJdbcLogger.java:145)- <==      Total: 1

同理,模拟用户id为2的用户登录。返回两条记录

2019-08-24 14:09:59,006 DEBUG (BaseJdbcLogger.java:145)- ==>  Preparing: select * from product where where name like '%iphone%' 
2019-08-24 14:09:59,017 DEBUG (BaseJdbcLogger.java:145)- ==> Parameters: 
2019-08-24 14:09:59,020 DEBUG (BaseJdbcLogger.java:145)- <==      Total: 2
⑥真实业务场景下的考虑的问题

大概的简易数据权限就这样了,下面多谈谈在真实业务场景下的一些情况:
1,不同的授权类型都是在数据库中进行维护,在不同的实现中,读取数据库的权限配置信息,进而封装sql
2,存在多表关联的时候,或非驱动表控制了数据权限,驱动表没有控制,根据不同的业务场景具体分析,对sql的处理也就更加困难了。
3,在分布式中存在跨系统问题的数据权限控制。得看情况,做方案。

感谢各位的阅读,以上就是“怎么通过MyBatis自定义插件实现简易数据权限”的内容了,经过本文的学习后,相信大家对怎么通过MyBatis自定义插件实现简易数据权限这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是创新互联,小编将为大家推送更多相关知识点的文章,欢迎关注!


文章名称:怎么通过MyBatis自定义插件实现简易数据权限
网站路径:http://ybzwz.com/article/pipopj.html