zoukankan      html  css  js  c++  java
  • 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("	", " ").replace("
    ", " ");
            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和基类都没有强制性要求

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

  • 相关阅读:
    Android 对话框(Dialog)
    struts2 开发模式
    Struts2动态方法调用(DMI)
    Struts2中 Path (getContextPath与basePath)
    String path = request.getContextPath
    ios虚拟机安装(二)
    Spring MVC 的 研发之路
    Swift辛格尔顿设计模式(SINGLETON)
    【算法导论】多项式求和
    uva 11181
  • 原文地址:https://www.cnblogs.com/shanzhai/p/10885401.html
Copyright © 2011-2022 走看看