每天每天
越来越爱

mybatis + springboot sql添加数据仅限 mybatis 实现数据仅限

  1. 解决的问题
    在项目开发中系统定义两种用户管理用户和企业用户,管理用户可以查看全部的数据,而企业用户只能看自己企业的数据,但是路由权限是一样的。就需要在数据库查询的时候添加相应的条件。>在项目开发中系统定义两种用户管理用户和企业用户,管理用户可以查看全部的数据,而企业用户只能看自己企业的数据,但是路由权限是一样的。就需要在数据库查询的时候添加相应的条件。

  1. 思路
    使用spring的拦截器,在mybatis执行SQL之前判断是否有权限和是否需要添加权限,没有就自动拼接条件。MyBatis-Plus给我们提供了一个DataPermissionHandler接口用于做数据权限控制,其核心调用逻辑位于DataPermissionInterceptor中,因此我们自己需要定义一个DataPermissionHandler实现类将其注入到MybatisPlusInterceptor中

定义是否需要验证权限的注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE,ElementType.Method)
@Documented
public @interface DataAuth(){
    boolean isAuth() default true;
}

定义数据权限配置对象

    @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;
 }
}

定义数据权限上下文

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();
 }
}

实现DataPermissionHandler接口

/**
 * 全局数据权限处理器
 *
 * @author Guo Shuai
 * @version 1.0
 * @date 2022/7/25
 */
public class GlobalDataAuthHandler implements DataPermissionHandler {

 @Override
 public Expression getSqlSegment(Expression where, String mappedStatementId) {
    if(null == where ) return;
     //从数据权限上下文获取数据权限参数列表
     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();
 }
}

将实现的拦截器注入到MybatisPlusInterceptor中

@Configuration
public class MybatisPlusConfig {

 /**
  * 分页插件和数据权限插件
  */
 @Bean
 public MybatisPlusInterceptor mybatisPlusInterceptor() {
     MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
     //数据权限
     interceptor.addInnerInterceptor(new DataPermissionInterceptor(new GlobalDataAuthHandler()));

     interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
     return interceptor;
 }
}

定义Spring拦截器处理数据权限上下文参数


@Component
public class DataAuthInterceptor implements HandlerInterceptor {

 @Override
 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
     // 获取用户登录信息
     LoginUser user = UserUtil.getLoginUser();
     // 获取是否有注解
     DataAuth auth = null;
     if(handle instanceof HandlerMethod){
        HandlerMethod method = (HandlerMethod) handler;
        DataAuth anno = method.getMethod().getDeclaringClass().getAnnotation(DataAuth.class);
        auth = anno == null ? method.getMethodAnnotation(DataAuth.class) : anno;
     }

     // 获取用户的角色或者是否有对应的权限,如果没有,则需要添加相应的限制
     ...

     if(没有权限 and auth.isAuth()){
     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();
 }
}

拦截器不生效解决

@Configuration
public class ResourcesConfig implements WebMvcConfigurer {
     @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new GlobalDataAuthHandler()).addPathPatterns("/**");
    }
}

  1. 使用
    在对应controller的方法或者类上添加 @DataAuth 注解

参考文章-思否-Pursuer丶

赞(0) 打赏

评论 抢沙发