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

一、场景介绍

在开发过程中很多时候咱们须要依据某些条件去做数据权限,比方: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结构来说应该能够实现大部分的数据权限过滤,这里数据类型只辨别了汇合和其余类型,若本人的业务有其余要求的话能够对其进行革新拓展。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理