mybatis整合数据权限

现在很多企业级应用都需要拦截数据权限, 只有配置了相应数据权限的人才能看到该数据

关于数据权限的实现, 个人想了两种实现方式

第一种是基于AOP, 配置相应的注解, 在切面中将数据权限的参数值强制设置到请求参数中去, 然后dao层利用mybatis的动态sql, 将权限值拼接进去, 该方案有前提条件, 数据权限控制的字段必须放到基类中, 其他的对象要继承该基类, Mapper.xml必须抽取一个公用的, 其他的Mapper需要引用该mapper文件作为权限控制(否则每个类和Mapper.xml都要独自维护一套, 不便于后续扩展和维护)

第二种是基于mybatis的拦截器, 拦截sql后将数据权限控制的sql拼接进去, 基于mybatis拦截器拼接sql的难点在于数据权限sql和原sql的拼接, 网上很多版本都是select * from (原sql) where 数据权限sql, 这种实际上并不可取, 一旦出现分页, 或者最终查出的结果集不包含权限控制字段的话, 就会出现bug

这里使用的是正则表达式去匹配后拼接权限sql, 其他的可以参考网上mybatis拦截器的方案

/**
 * sql解析器
 *
 * @author wang.js on 2019/5/8.
 * @version 1.0
 */
public class SqlParser {

    private SqlParser() {
    }

    private static final String SQL_WHERE = "WHERE";

    private static final String SQL_LEFT_JOIN = "LEFT JOIN ";

    /**
     * 将数据权限的sql拼进原sql中
     *
     * @param originSql    原sql
     * @param privilegeSql 数据权限sql
     * @return String
     */
    public static String handlerSql(String originSql, String privilegeSql) {
        if (originSql.endsWith(";")) {
            originSql = originSql.substring(0, originSql.lastIndexOf(";"));
        }
        originSql = originSql.replace("\t", " ").replace("\n", " ");
        originSql = originSql.replaceAll(" {2,}", " ");
        originSql = originSql.replaceAll(" where ", " " + SQL_WHERE + " ");
        originSql = addWhere(originSql);
        originSql = originSql.replace("left", "LEFT");
        originSql = originSql.replace("join", "JOIN");
        originSql = originSql.replaceAll("LEFT[ ]+JOIN[ ]+", SQL_LEFT_JOIN);

        List<String> matcherList = matcherTableSql(originSql);
        for (String matcherSql : matcherList) {
            if (originSql.contains(SQL_LEFT_JOIN + matcherSql)) {
                continue;
            }
            String newMatcherSql = mergeSql(matcherSql, privilegeSql);
            originSql = originSql.replace(matcherSql, newMatcherSql);
        }
        return originSql;
    }

    /**
     * 添加where关键字
     *
     * @param originSql 原始sql
     * @return String
     */
    private static String addWhere(String originSql) {
        if (originSql.contains("WHERE")) {
            return originSql;
        }
        String[] split = originSql.split(" ");
        for (int i = 0; i < split.length; i++) {
            if (split[i].equalsIgnoreCase("GROUP") && (i + 1) < split.length && split[i + 1].equalsIgnoreCase("BY")) {
                return originSql.replaceAll(split[i], "WHERE 1=1 GROUP");
            }
            if (split[i].equalsIgnoreCase("ORDER") && (i + 1) < split.length && split[i + 1].equalsIgnoreCase("BY")) {
                return originSql.replaceAll(split[i], "WHERE 1=1 ORDER");
            }
            if (split[i].equalsIgnoreCase("LIMIT")) {
                return originSql.replaceAll(split[i], "WHERE 1=1 LIMIT");
            }
        }
        return originSql + " WHERE 1=1";
    }

    /**
     * 合并sql
     *
     * @param originSql    原sql
     * @param privilegeSql 数据权限sql
     * @return String
     */
    private static String mergeSql(String originSql, String privilegeSql) {
        return originSql.replace(SQL_WHERE, SQL_WHERE + " " + privilegeSql);
    }

    /**
     * 配置符合
     *
     * @param originSql 原sql
     * @return List<String>
     */
    private static List<String> matcherTableSql(String originSql) {
        List<String> matcharList = new ArrayList<>();
        String regex = "\\w[ ,](.*?)WHERE";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(originSql);
        while (matcher.find()) {
            matcharList.add(matcher.group());
        }
        return matcharList;
    }

}

写个测试类

@Test
public void t1() {
    String privilegeSql = " brand_code in (1, 2, 3) and region_code in (2, 3) and";
    String originSql = "select * from (select t1, t2, t3 from tableA where 1=1) t1, (select t1, t2, t3 from tableB s where 1=1) t2";
    //      String originSql = "select t1, t2, t3 from tableA";
    System.out.println(SqlParser.handlerSql(originSql, privilegeSql));
}

测试后输出的结果为 select * from (select t1, t2, t3 from tableA WHERE brandcode in (1, 2, 3) and regioncode in (2, 3) and 1=1) t1, (select t1, t2, t3 from tableB s WHERE brandcode in (1, 2, 3) and regioncode in (2, 3) and 1=1) t2

这里需要强调的是, 必须保证每条需要权限控制的语句都含有where关键字, 否则数据权限sql拼接不进去

总结 第一种基于AOP的方案需要维护基类和Mapper的通用代码, 其他的必须继承或引用这两个文件, 但是好处是不容易出现bug

第二种基于mybatis拦截器的方案需要保证每条需要权限控制的sql都有where关键字, 好处是mapper.xml和基类都没有强制性要求

具体使用哪一种就看实际项目需要了

原文地址:https://www.cnblogs.com/shanzhai/p/10885401.html

时间: 2024-11-05 12:34:59

mybatis整合数据权限的相关文章

Mybatis整合Spring 【转】

根据官方的说法,在ibatis3,也就是Mybatis3问世之前,Spring3的开发工作就已经完成了,所以Spring3中还是没有对Mybatis3的支持.因此由Mybatis社区自己开发了一个Mybatis-Spring用来满足Mybatis用户整合Spring的需求.下面就将通过Mybatis-Spring来整合Mybatis跟Spring的用法做一个简单的介绍. MapperFactoryBean 首先,我们需要从Mybatis官网上下载Mybatis-Spring的jar包添加到我们项

springmvc+spring+mybatis整合实例【转】

开发环境: System:Windows server 2003 WebBrowser:IE6+.Firefox3+ JavaEE Server:tomcat5. IDE:eclipse.MyEclipse 6.5 Database:MySQL 开发依赖库: JavaEE5.Spring 3.0.5.Mybatis 3.0.2.myBatis-spring-1.0.0-rc2 1. 首先新建一个WebProject 命名为ssi,新建项目时,使用JavaEE5的lib库.然后手动添加需要的jar

Spring、Spring MVC、MyBatis整合文件配置详解

使用SSM框架做了几个小项目了,感觉还不错是时候总结一下了.先总结一下SSM整合的文件配置.其实具体的用法最好还是看官方文档. Spring:http://spring.io/docs MyBatis:http://mybatis.github.io/mybatis-3/ 基本的组织结构和用法就不说了,前面的博客和官方文档上都非常的全面.jar包可以使用Maven来组织管理.来看配置文件. web.xml的配置                                           

如何在应用系统中实现数据权限的控制功能(2)

关于数据权限的控制,可能我们在做很多大型一点的系统都会碰到过,可能每个人设计和解决问题的思路都有所不同,本文介绍我自己框架里面的解决思路.从上一篇<如何在应用系统中实现数据权限的控制功能>里面我们可能对权限控制和数据权限的控制有了一个初步的了解,本文接着进一步介绍在应用系统中,如何集成数据权限的控制功能. 1.数据权限实现思路分析 为了实现数据权限的控制,我们需要在通用的权限系统里面保存好对应角色具有哪些组织机构的数据权限,然后在应用系统中调用API进行过滤数据处理即可. 为了实现以上的功能需

SpringMVC + MyBatis整合 【转】

环境:spring3.1.1+mybatis3.2.8+mybatis-spring1.2.3 网络上关于这个架构的搭建文章,实在是太多了,本文是对于本人初次搭建时的一些注意点的整理. 主要是一些配置文件的内容和架构的目录. 0. project 目录 1. spring-resources.xml 这个文件是用来完成spring和mybatis的整合的xml.注意properties文件的读入方式. <?xml version="1.0" encoding="UTF-

Spring+MyBatis整合

1.创建一个web工程.工程名为ssm 2.导入Spring,MyBatis,Oracle和MySQL以及MyBatis提供的与Spring整合的插件包 mysql的jar文件:mysql-connector-java-5.1.7-bin.jar oracle的jar文件:ojdbc5.jar c3p0的jar文件:c3p0-0.9.1.2.jar mybatis的jar文件: asm-3.3.1.jar cglib-2.2.2.jar commons-logging-1.1.1.jar log

SpringMVC与MyBatis整合之日期格式转换

在上一篇博客<SpringMVC与MyBatis整合(一)——查询人员列表>中遗留了日期格式转换的问题,在这篇记录解决过程. 对于controller形参中pojo对象,如果属性中有日期类型,需要自定义参数绑定.         将请求日期数据串传成 日期类型,要转换的日期类型和pojo中日期属性的类型保持一致. 在上一篇的示例程序中,Person类属性如下: 而测试结果如下: 所以自定义参数绑定将日期串转成java.util.Date类型.需要向处理器适配器中注入自定义的参数绑定组件. 添加

SpringMVC+Spring+Mybatis整合,使用druid连接池,声明式事务,maven配置

一直对springmvc和mybatis挺怀念的,最近想自己再搭建下框架,然后写点什么. 暂时没有整合缓存,druid也没有做ip地址的过滤.Spring的AOP简单配置了下,也还没具体弄,不知道能不能用,log也不知道能不能用,`(*∩_∩*)′哈哈,有点不负责任...... 直接上代码: 使用的eclipse和eclipse自带的maven,参考了网上的资料,有些代码是拷贝的,不过都自己测试过了.嗯,可以跑起来... 先上项目结构: 新建maven项目,选择web,然后配置pom: <pro

[手把手教程][JavaWeb]优雅的SpringMvc+Mybatis整合之路

来源于:http://www.jianshu.com/p/5124eef40bf0 [手把手教程][JavaWeb]优雅的SpringMvc+Mybatis整合之路 手把手教你整合最优雅SSM框架:SpringMVC + Spring + MyBatis 前面网友说我为啥很久不更新博客了,我告诉他们我准备潜修.其实是我的博客被人批评是在记流水账(~一脸尴尬~). 再次安利一波,博客地址:acheng1314.cn 本文中的图片用了个人服务器存储,网速较慢,各位老司机耐心等待. 工具 IDE为id