关于java:MyBatisPlus结合Spring-Boot实现数据权限

61次阅读

共计 4053 个字符,预计需要花费 11 分钟才能阅读完成。

一、场景介绍

在开发过程中很多时候咱们须要依据某些条件去做数据权限,比方:A 组织只能看见 A 组织及其上司组织的数据,B 部门只能看见本人的数据、等等,此时如果每次都去本人写 SQL 进行校验就会显得代码十分臃肿,因为就产生了本人去定义一套全局专用的数据权限过滤形式。

二、实现思路

借助于 Spring 的拦截器或过滤器,当申请进入到 Controller 时,将该用户的数据权限信息存入数据权限上下文中,在 MyBatis 执行 SQL 之前将其动静拼接下来,MyBatis-Plus 给咱们提供了一个 DataPermissionHandler 接口用于做数据权限管制,其外围调用逻辑位于 DataPermissionInterceptor 中,因而咱们本人须要定义一个 DataPermissionHandler 实现类将其注入到 MybatisPlusInterceptor 中

三、实现步骤

  1. 定义数据权限配置对象

    @Data
    public class DataAuthConfig {
    
     private Map<String, Object> params;
    
     private DataAuthConfig() {}
    
     /**
      * 构建实例
      *
      * @return com.minportal.platform.common.mybatis.core.DataPermissionContextHolder
      * @author Guo Shuai
      * @date 2022/7/25
      * @since 1.0
      **/
     public static DataAuthConfig create() {DataAuthConfig config = new DataAuthConfig();
         config.setParams(new ConcurrentHashMap<>(4));
         return config;
     }
    
     /**
      * 增加参数
      *
      * @param key   sql 字段名称
      * @param value 值
      * @return java.util.Map<java.lang.String, java.lang.Object>
      * @author Guo Shuai
      * @date 2022/7/25
      * @since 1.0
      **/
     public DataAuthConfig withParam(String key, Object value) {
         // 设置参数
         params.put(key, value);
         // 增加参数
         return this;
     }
    }
  2. 定义数据权限上下文

    public class DataAuthContextHolder {private static final ThreadLocal<DataAuthConfig> CONTEXT = new ThreadLocal<>();
    
     /**
      * 获取配置
      *
      * @return java.util.Map<java.lang.String, java.lang.Object>
      * @author Guo Shuai
      * @date 2022/7/25
      * @since 1.0
      **/
     public static DataAuthConfig getContext() {if (Objects.isNull(CONTEXT.get())) {synchronized (DataAuthContextHolder.class) {
                 // 判断是否为空
                 if (Objects.isNull(CONTEXT.get())) {CONTEXT.set(DataAuthConfig.create());
                 }
             }
         }
         // 返回
         return CONTEXT.get();}
    
     /**
      * 清空数据
      *
      * @author Guo Shuai
      * @date 2022/7/25
      * @since 1.0
      **/
     public static void clean() {CONTEXT.remove();
     }
    }
  3. 实现 DataPermissionHandler 接口

    /**
     * 全局数据权限处理器
     *
     * @author Guo Shuai
     * @version 1.0
     * @date 2022/7/25
     */
    public class GlobalDataAuthHandler implements DataPermissionHandler {
    
     @Override
     public Expression getSqlSegment(Expression where, String mappedStatementId) {
         // 从数据权限上下文获取数据权限参数列表
         DataAuthConfig config = DataAuthContextHolder.getContext();
         // 判断是否为空
         if (config == null || CollUtil.isEmpty(config.getParams())) {return where;} else {return dataScopeFilter(where, config.getParams());
         }
     }
    
     /**
      * 构建过滤条件
      *
      * @param where      条件对象
      * @param conditions 条件列表
      * @return net.sf.jsqlparser.expression.Expression
      * @author Guo Shuai
      * @date 2022/7/25
      * @since 1.0
      **/
     public static Expression dataScopeFilter(Expression where, Map<String, Object> conditions) {
         // 定义条件
         AtomicReference<Expression> whereAtomic = new AtomicReference<>(where);
         // 循环结构条件
         conditions.forEach((key, value) -> {
             // 判断 value 的类型(汇合非凡解决)if (value instanceof Collection) {Collection<?> collection = (Collection<?>) value;
                 InExpression expression = new InExpression();
                 expression.setLeftExpression(new Column(key));
                 // 获取条件
                 ItemsList itemsList = new ExpressionList(collection.stream().map(String::valueOf).map(StringValue::new).collect(Collectors.toList()));
                 expression.setRightItemsList(itemsList);
                 // 拼接条件
                 whereAtomic.set(new AndExpression(whereAtomic.get(), expression));
             } else {whereAtomic.set(new AndExpression(whereAtomic.get(), new EqualsTo(new Column(key), new StringValue(String.valueOf(value)))));
             }
         });
    
         return whereAtomic.get();}
    }
  4. 将实现的拦截器注入到 MybatisPlusInterceptor 中

    @Configuration
    public class MybatisPlusConfig {
    
     /**
      * 分页插件和数据权限插件
      */
     @Bean
     public MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
         // 数据权限
         interceptor.addInnerInterceptor(new DataPermissionInterceptor(new GlobalDataAuthHandler()));
    
         return interceptor;
     }
    }
  5. 定义 Spring 拦截器解决数据权限上下文参数

    @Component
    public class DataAuthInterceptor implements HandlerInterceptor {
    
     @Override
     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
         // 设置数据权限信息
         // 例 A 部门只能看本人的数据及其子级部门 A1 的数据
         DataAuthContextHolder.getContext().withParam("organId", Lists.newArrayList("A", "A1"));
         return HandlerInterceptor.super.preHandle(request, response, handler);
     }
    
     @Override
     public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
     }
    
     @Override
     public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {DataAuthContextHolder.clean();
     }
    }

    四、总结

    对于不是特地简单的 SQL 结构来说应该能够实现大部分的数据权限过滤,这里数据类型只辨别了汇合和其余类型,若本人的业务有其余要求的话能够对其进行革新拓展。

正文完
 0