Springboot+mybatis-plus+注解实现数据权限隔离

 更新时间:2021年07月06日 08:54:30   作者:我与黎明道晚安  
本文将结合实例代码,介绍Springboot+mybatis-plus+注解实现数据权限隔离,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧

1.创建注解

当此注解打在类上,不需要传参,该类下所有查询接口开启数据隔离;打在方法上默认开启数据隔离,传参为false则该方法关闭验证

/**
 * 数据权限验证注解
 * @author xiaohua
 * @date 2021/6/23
 */
@Documented
@Target({METHOD, ANNOTATION_TYPE, TYPE})
@Retention(RUNTIME)
public @interface DataPermission {
    /**
     * 是否要进行数据权限隔离
     */
    boolean isPermi() default true;
}

2. 具体实现

@Component
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class DataPermissionInterceptor implements Interceptor {
    private static final Logger logger = LoggerFactory.getLogger(DataPermissionInterceptor.class);

    @Autowired
    private TokenService tokenService;

    //扫描的包路径(根据自己的项目路径来),这里是取的配置里的包路径
    @Value("${permission.package-path}")
    private String packagePath;

    private final static String DEPT_ID = "dept_id";

    private final static String USER_ID = "create_user";

    private static List<String> classNames;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        try {
            LoginInfo user = tokenService.getLoginInfo();
            if (user == null){
                return invocation.proceed();
            }

            List<Long> deptIds = (List<Long>) Convert.toList(user.getDataScope());
            if (deptIds == null){
                deptIds = new ArrayList<>();
            }
            //反射扫包会比较慢,这里做了个懒加载
            if (classNames == null) {
                synchronized (LazyInit.class){
                    if (classNames == null){
                        //扫描指定包路径下所有包含指定注解的类
                        Set<Class<?>> classSet = ClassUtil.scanPackageByAnnotation(packagePath, DataPermission.class);
                        if (classSet == null && classSet.size() == 0){
                            classNames = new ArrayList<>();
                        } else {
                            //取得类全名
                            classNames =  classSet.stream().map(Class::getName).collect(Collectors.toList());
                        }
                    }
                }
            }

            // 拿到mybatis的一些对象
            StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());
            MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
            MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");

            // mappedStatement.getId()为执行的mapper方法的全路径名,newId为执行的mapper方法的类全名
            String newId = mappedStatement.getId().substring(0, mappedStatement.getId().lastIndexOf("."));
            // 如果不是指定的方法,直接结束拦截
            if (!classNames.contains(newId)) {
                return invocation.proceed();
            }
            String newName = mappedStatement.getId().substring(mappedStatement.getId().lastIndexOf(".") + 1, mappedStatement.getId().length());
            //是否开启数据权限
            boolean isPermi = true;
            Class<?> clazz = Class.forName(newId);
            //遍历方法
            for (Method method : clazz.getDeclaredMethods()) {
                //方法是否含有DataPermission注解,如果含有注解则将数据结果过滤
                if (method.isAnnotationPresent(DataPermission.class) && newName.equals(method.getName())) {
                    DataPermission dataPermission =  method.getAnnotation(DataPermission.class);
                    if (dataPermission != null) {
                        //不验证
                        if (!dataPermission.isPermi()) {
                            isPermi = false;
                        } else { //开启验证
                            isPermi = true;
                        }
                    }
                }
            }

            if (isPermi){
                // 获取到原始sql语句
                String sql = statementHandler.getBoundSql().getSql();

                // 解析并返回新的SQL语句,只处理查询sql
                if (mappedStatement.getSqlCommandType().toString().equals("SELECT")) {
    //                    String newSql = getNewSql(sql,deptIds,user.getUserId());
                    sql = getSql(sql,deptIds,user.getUserId());
                }
                // 修改sql
                metaObject.setValue("delegate.boundSql.sql", sql);
            }
            return invocation.proceed();
        } catch (Exception e){
            logger.error("数据权限隔离异常:", e);
            return invocation.proceed();
        }

    }
    
    
    /**
     * 解析SQL语句,并返回新的SQL语句
     * 注意,该方法使用了JSqlParser来操作SQL,该依赖包Mybatis-plus已经集成了。如果要单独使用,请先自行导入依赖
     *
     * @param sql 原SQL
     * @return 新SQL
     */
    private String getSql(String sql,List<Long> deptIds,Long userId) {

        try {
            String condition = "";
            String permissionSql = "(";
            if (deptIds.size() > 0){
                for (Long deptId : deptIds) {
                    if ("(".equals(permissionSql)){
                        permissionSql = permissionSql + deptId;
                    } else {
                        permissionSql = permissionSql + "," + deptId;
                    }
                }
                permissionSql = permissionSql + ")";
                // 修改原语句
                condition = DEPT_ID +" in " + permissionSql;
            } else {
                condition = USER_ID +" = " + userId;
            }

            if (StringUtils.isBlank(condition)){
                return sql;
            }
            Select select = (Select)CCJSqlParserUtil.parse(sql);
            PlainSelect plainSelect = (PlainSelect)select.getSelectBody();
            //取得原SQL的where条件
            final Expression expression = plainSelect.getWhere();
            //增加新的where条件
            final Expression envCondition = CCJSqlParserUtil.parseCondExpression(condition);
            if (expression == null) {
                plainSelect.setWhere(envCondition);
            } else {
                AndExpression andExpression = new AndExpression(expression, envCondition);
                plainSelect.setWhere(andExpression);
            }
            return plainSelect.toString();
        } catch (JSQLParserException e) {
            logger.error("解析原SQL并构建新SQL错误:" + e);
            return sql;
        }
    }

到此这篇关于Springboot+mybatis-plus+注解实现数据权限隔离的文章就介绍到这了,更多相关Springboot+mybatis-plus+注解实现数据权限隔离内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! 

相关文章

  • 浅谈Java并发中的内存模型

    浅谈Java并发中的内存模型

    这篇文章主要介绍了Java并发中的内存模型,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-05-05
  • java中Properties文件加载和使用方法

    java中Properties文件加载和使用方法

    这篇文章主要为大家详细介绍了java中Properties文件加载和使用方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-03-03
  • Spring Security架构以及源码详析

    Spring Security架构以及源码详析

    这篇文章主要给大家介绍了关于Spring Security架构以及源码的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-06-06
  • java打包文件成zip、压缩文件及file.mkdir和mkdirs的区别详解

    java打包文件成zip、压缩文件及file.mkdir和mkdirs的区别详解

    这篇文章主要给大家介绍了关于java打包文件成zip、压缩文件及file.mkdir和mkdirs区别详解的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-12-12
  • Java读取Properties配置文件的6种方式汇总

    Java读取Properties配置文件的6种方式汇总

    这篇文章主要给大家介绍了关于Java读取Properties配置文件的6种方式,java中的properties文件是一种配置文件,主要用于表达配置信息,文中通过示例代码介绍的非常详细,需要的朋友可以参考下
    2023-07-07
  • Java执行cmd命令两种实现方法解析

    Java执行cmd命令两种实现方法解析

    这篇文章主要介绍了Java执行cmd命令两种实现方法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-07-07
  • Java通过导出超大Excel文件解决内存溢出问题

    Java通过导出超大Excel文件解决内存溢出问题

    导出excel是咱Java开发的必备技能,下面这篇文章主要给大家介绍了关于Java通过导出超大Excel文件解决内存溢出问题的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下
    2021-09-09
  • Java教程package和import访问控制的步骤详解

    Java教程package和import访问控制的步骤详解

    这篇文章主要为大家介绍了Java教程package和import访问控制的步骤详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-06-06
  • SpringBoot常用注解详细整理

    SpringBoot常用注解详细整理

    大家好,本篇文章主要讲的是SpringBoot常用注解详细整理,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下,方便下次浏览
    2021-12-12
  • PageHelper在springboot+mybatis框架中的使用步骤及原理解析

    PageHelper在springboot+mybatis框架中的使用步骤及原理解析

    这篇文章主要介绍了PageHelper在springboot+mybatis框架中的使用步骤及原理解析,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-03-03

最新评论