基于Mybatis-Plus的数据权限框架

本文介绍了如何基于Mybatis-Plus构建数据权限框架,包括设计思路、自定义SQL解析器、切面处理等关键步骤,实现了数据权限的自动化过滤,适配Shiro框架。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

近期开发使用的框架基本上都是springboot + Mybatis-plus 方便,快速。而且差不多都是单表查询,很少使用关联表。

经常会遇到需要做数据权限过滤的查询,每次都要写SQL,然后使用in的方式。非常麻烦。所以研究了一下Mybatis-plus的进阶使用。可以通过自定义SQL解析器,自定义SQL内容。

使用该方式 + 切面 的方式,实现自动添加数据权限的过滤方式。

注:我自己的工程的权限框架为shiro,可以通过修改,配置适配自己框架

设计

因为数据权限是与请求地址相关,所以将菜单与数据权限关联,角色不直接关联数据权限,角色通过菜单间接关联数据权限。如果一个用户有多个角色,并且这些角色都有分配同一个菜单的不同数据权限,则以最大权限为主。

这里就不做过多介绍。主要还是说明MP的设计结构。

总体工程结构如下:其中DataScopeAspect为自定义切面,将数据权限设置到参数的父类属性中。
在这里插入图片描述

正文

pom依赖

3.4以前的版本配置不一样,请自行查看。

<dependencies>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-core</artifactId>
        <version>3.5.2</version>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-extension</artifactId>
        <version>3.5.2</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.16</version>
    </dependency>
</dependencies>

1.数据权限基类

所有需要做数据权限过滤的接口入参,都需要继承该类,用于存储机构编号列表或者用户编号

package com.hongtool.mybatis.perms.domain;

import java.io.Serializable;
import java.util.List;

/**
 * <p> 数据权限 传参 </p>
 *
 * @author hongtool
 * @date 2022/11/1
 */
public class DataScopePerms implements Serializable {
    
    private static final long serialVersionUID = 1L;
    
    /** 创建者 */
    private String userPerm;
    
    /** 机构部门 */
    private List<Long> deptPerms;
    
    public DataScopePerms() {
    
    }
    // TODO 省略 GET 和 SET 方法
}

2.权限字段注解

注: 如果有更多的权限类型,则可以再自定义添加

1)机构部门权限

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * <p> 部门权限(只能有一个,如果有多个,只会获取第一个) </p>
 *
 * @author hongtool
 * @date 2022/11/24
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
public @interface DeptPerm {
    // 字段名,如果为空,则从MyBatis-plus的解析表字段获取
    String value() default "";
}

2)用户权限

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * <p> 用户权限(只能有一个,同机构部门权限) </p>
 *
 * @author hongtool
 * @date 2022/11/24
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
public @interface UserPerm {
    String value() default "";
}

3.自定义SQL方法

如果有额外的自定义SQL,则可以添加。

package com.hongtool.mybatis.perms.enums;

/**
 * <p> 自定义本项目支持的SQL方法 </p>
 * 模拟MyBatis-plus的 {@link com.baomidou.mybatisplus.core.enums.SqlMethod}
 * @author hongtool
 * @date 2022/11/1
 */
public enum HongToolSqlMethod {
    
    COUNT_PERMISSION("countPerms", "查询满足条件总记录数(带权限控制)", "<script>\nSELECT COUNT(%s) FROM %s %s %s\n</script>"),
    LIST_PERMISSION("listPerms", "查询满足条件所有数据(带权限控制)", "<script>\nSELECT %s FROM %s %s %s\n</script>"),
    PAGE_PERMISSION("pagePerms", "查询满足条件所有数据(并翻页)(带权限控制)", "<script>\nSELECT %s FROM %s %s %s\n</script>"),
   
    ;
    private final String method;
    private final String desc;
    private final String sql;
    
    HongToolSqlMethod(String method, String desc, String sql) {
        this.method = method;
        this.desc = desc;
        this.sql = sql;
    }
    
    public String getMethod() {
        return method;
    }
    
    public String getDesc() {
        return desc;
    }
    
    public String getSql() {
        return sql;
    }
   
}

4.自定义注入器

package com.hongtool.mybatis.perms.injector;

import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.hongtool.mybatis.perms.injector.methods.SelectCountPermission;
import com.hongtool.mybatis.perms.injector.methods.SelectListPermission;
import com.hongtool.mybatis.perms.injector.methods.SelectPagePermission;

import java.util.List;

/**
 * <p> mybatis 扩展,为了查询有权限的数据 </p>
 *
 * @author hongtool
 * @date 2022/11/1
 */
public class HongToolInjector extends DefaultSqlInjector {
    
    @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
        List<AbstractMethod> methodList = super.getMethodList(mapperClass, tableInfo);
        // 通过权限,查询数据
        methodList.add(new SelectListPermission());
        methodList.add(new SelectPagePermission());
        methodList.add(new SelectCountPermission());
        return methodList;
    }
}

1)有权限的列表查询

import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.hongtool.mybatis.perms.enums.HongToolSqlMethod;
import com.hongtool.mybatis.perms.util.WherePermSqlUtil;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;

public class SelectListPermission extends AbstractMethod {
    
    public SelectListPermission() {
        super(HongToolSqlMethod.LIST_PERMISSION.getMethod());
    }
    
    public  SelectListPermission(String methodName) {
        super(methodName);
    }

    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        HongToolSqlMethod sqlMethod = HongToolSqlMethod.LIST_PERMISSION;
        String sql = String.format(sqlMethod.getSql(), sqlSelectColumns(tableInfo, true),
                tableInfo.getTableName(), WherePermSqlUtil.getWhereSql(tableInfo),
                sqlComment());
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        return this.addSelectMappedStatementForTable(mapperClass, sqlMethod.getMethod(), sqlSource, tableInfo);
    }
}

2)有权限的分页查询

import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.hongtool.mybatis.perms.enums.HongToolSqlMethod;
import com.hongtool.mybatis.perms.util.WherePermSqlUtil;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;

public class SelectPagePermission extends AbstractMethod {
    
    public SelectPagePermission() {
        super(HongToolSqlMethod.PAGE_PERMISSION.getMethod());
    }
    
    public  SelectPagePermission(String methodName) {
        super(methodName);
    }
    
    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        HongToolSqlMethod sqlMethod = HongToolSqlMethod.PAGE_PERMISSION;
        String sql = String.format(sqlMethod.getSql(), sqlSelectColumns(tableInfo, true),
                tableInfo.getTableName(), WherePermSqlUtil.getWhereSql(tableInfo),
                sqlComment());
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        return this.addSelectMappedStatementForTable(mapperClass, sqlMethod.getMethod(), sqlSource, tableInfo);
    }
}

3)有权限的数量统计

import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.hongtool.mybatis.perms.enums.HongToolSqlMethod;
import com.hongtool.mybatis.perms.util.WherePermSqlUtil;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;

public class SelectCountPermission extends AbstractMethod {
    
    public SelectCountPermission() {
        super(HongToolSqlMethod.COUNT_PERMISSION.getMethod());
    }
    
    public  SelectCountPermission(String methodName) {
        super(methodName);
    }
    
    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        HongToolSqlMethod sqlMethod = HongToolSqlMethod.COUNT_PERMISSION;
        String sql = String.format(sqlMethod.getSql(), this.sqlCount(), tableInfo.getTableName(),
                WherePermSqlUtil.getWhereSql(tableInfo), sqlComment());
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        return this.addSelectMappedStatementForOther(mapperClass, sqlMethod.getMethod(), sqlSource, Integer.class);
    }
}

4)WherePermSqlUtil Sql组装工具(重点代码)

部分参考MP源码的写法,有待优化。

TableInfo中获取实体对象,然后通过反射获取字段的DeptPermUserPerm注解,确定数据权限过滤字段。如果没有,则不做过滤。

import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;
import com.hongtool.mybatis.perms.annotation.DeptPerm;
import com.hongtool.mybatis.perms.annotation.UserPerm;

import java.lang.reflect.Field;

/**
 * <p> 权限 whereSQL 工具类 </p>
 *
 * @author hongtool
 * @date 2022/11/9
 */
public class WherePermSqlUtil {
    
    public static String getWhereSql(TableInfo table) {
        String sqlScript;
        String userField = getUserPermField(table);
        String deptField = getDeptPermField(table);
        if (table.isWithLogicDelete()) {
            sqlScript = table.getAllSqlWhere(true, true, "ew.entity.");
            sqlScript = SqlScriptUtils.convertIf(sqlScript, String.format("%s != null", "ew.entity"), true)+ "\n";
            // 添加数据权限
            sqlScript = sqlScript + getUserPermsSql(userField);
            sqlScript = sqlScript + getDeptPermsSql(deptField);
            sqlScript = sqlScript + "\n" + table.getLogicDeleteSql(true, true) + "\n";
            String normalSqlScript = SqlScriptUtils.convertIf(String.format("AND ${%s}", "ew.sqlSegment"), String.format("%s != null and %s != '' and %s", "ew.sqlSegment", "ew.sqlSegment", "ew.nonEmptyOfNormal"), true);
            normalSqlScript = normalSqlScript + "\n";
            normalSqlScript = normalSqlScript + SqlScriptUtils.convertIf(String.format(" ${%s}", "ew.sqlSegment"), String.format("%s != null and %s != '' and %s", "ew.sqlSegment", "ew.sqlSegment", "ew.emptyOfNormal"), true);
            sqlScript = sqlScript + normalSqlScript;
            sqlScript = SqlScriptUtils.convertChoose(String.format("%s != null", "ew"), sqlScript, table.getLogicDeleteSql(false, true));
            sqlScript = SqlScriptUtils.convertWhere(sqlScript);
            return "\n" + sqlScript;
        } else {
            sqlScript = table.getAllSqlWhere(false, true, "ew.entity.");
            sqlScript = SqlScriptUtils.convertIf(sqlScript, String.format("%s != null", "ew.entity"), true) + "\n";
            // 添加数据权限
            sqlScript = sqlScript + getUserPermsSql(getUserPermField(table));
            sqlScript = sqlScript + getDeptPermsSql(getDeptPermField(table));
            sqlScript = sqlScript + "\n";
            sqlScript = sqlScript + SqlScriptUtils.convertIf(String.format(SqlScriptUtils.convertIf(" AND ", String.format("%s and %s", "ew.nonEmptyOfEntity", "ew.nonEmptyOfNormal"), false) + "<if test="!(ew.nonEmptyOfEntity and ew.nonEmptyOfNormal) and data != null and (data.userPerm != null or data.userPerm != '' or data.deptPerms != null or data.deptPerms.size() > 0)"> AND </if> ${%s}", "ew.sqlSegment"), String.format("%s != null and %s != '' and %s", "ew.sqlSegment", "ew.sqlSegment", "ew.nonEmptyOfWhere"), true);
            sqlScript = SqlScriptUtils.convertWhere(sqlScript) + "\n";
            sqlScript = sqlScript + SqlScriptUtils.convertIf(String.format(" ${%s}", "ew.sqlSegment"), String.format("%s != null and %s != '' and %s", "ew.sqlSegment", "ew.sqlSegment", "ew.emptyOfWhere"), true);
            sqlScript = SqlScriptUtils.convertIf(sqlScript, String.format("%s != null", "ew"), true);
            return "\n" + sqlScript;
        }
    }
    
    private static String getUserPermsSql(String userField) {
        if(userField != null) {
            return "<if test="data != null">\n" +
                   "       <if test="data.userPerm != null">\n" +
                   "           <if test="ew == null">AND</if> " + userField + " = #{data.userPerm}\n" +
                   "       </if>\n" +
                   "</if>\n";
        }
        return "";
    }
    
    private static String getDeptPermsSql(String deptField) {
        if(deptField != null) {
            return "<if test="data != null">\n" +
                    "       <if test="data.deptPerms != null and data.deptPerms.size() > 0">\n" +
                    "           <if test="ew == null">AND</if> " +  deptField + " in \n" +
                    "               <foreach item="id" collection="data.deptPerms" open="(" separator="," close=")">\n" +
                    "                   #{id}\n" +
                    "               </foreach>\n" +
                    "       </if>\n" +
                    "</if>\n";
        }
        return "";
    }
    
    private static String getUserPermField(TableInfo tableInfo) {
        Field[] fields = tableInfo.getEntityType().getDeclaredFields();
        for (Field field : fields) {
            UserPerm userPerm = field.getAnnotation(UserPerm.class);
            if(userPerm != null) {
                String value = userPerm.value();
                if(StringUtils.isNotEmpty(value)) {
                    return value;
                } else {
                    return tableInfo.getFieldList().stream()
                            .filter(f -> f.getProperty().equals(field.getName()))
                            .findFirst()
                            .map(TableFieldInfo::getColumn).orElse(null);
                }
            }
        }
        return null;
    }
    
    private static String getDeptPermField(TableInfo tableInfo) {
        Field[] fields = tableInfo.getEntityType().getDeclaredFields();
        for (Field field : fields) {
            DeptPerm deptPerm = field.getAnnotation(DeptPerm.class);
            if(deptPerm != null) {
                String value = deptPerm.value();
                if(StringUtils.isNotEmpty(value)) {
                    return value;
                } else {
                   return tableInfo.getFieldList().stream()
                            .filter(f -> f.getProperty().equals(field.getName()))
                            .findFirst()
                            .map(TableFieldInfo::getColumn)
                            // 如果在字段中找不到,判断是否为主键字段
                            .orElse(tableInfo.getKeyProperty().equals(field.getName()) ? tableInfo.getKeyColumn() : null);
                }
            }
        }
        return null;
    }
   
}

5)添加自定义注入器到配置中

package com.hongtool.springframework.mybatis.config;

import com.baomidou.mybatisplus.core.injector.ISqlInjector;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.hongtool.mybatis.perms.injector.HongToolInjector;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;

/**
 * <p> 配置分页插件 </p>
 *
 * @author hongtool
 * @date 2022/9/16
 */
@EnableTransactionManagement
@Configuration
@MapperScan("com.hongtool.**.mapper")
public class MybatisPlusConfig {
    
    /**
     * 3.4.0 以后的配置方式
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 乐观锁
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        // 分页配置
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return interceptor;
    }
    
    /**
     * 自定义sql注入器
     * 或者application.properties配置:
     * mybatis-plus.globalConfig.sqlInjector=com.hongtool.mybatis.perms.injector.HongToolInjector
     */
    @Bean
    public ISqlInjector iSqlInjector() {
        return new HongToolInjector();
    }
}

5.自定义BaseMapper

方法名需要与HongToolSqlMethod的method参数的内容一致才能使用。不然会报错(找不到解析方法)。

package com.hongtool.mybatis.perms.mapper;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.hongtool.mybatis.perms.domain.DataScopePerms;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * 功能描述: 自定义项目BaseMapper <br/>
 */
public interface HongToolBaseMapper<T> extends BaseMapper<T> {
    
    /**
     * 功能描述: 带权限的查询 <br/>
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     * @return "java.util.List<T>"
     */
    <D extends DataScopePerms> List<T> listPerms(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper, @Param("data") D d);

    /**
     * 功能描述: 带权限的分页查询 <br/>
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     * @return "java.util.List<T>"
     */
    <D extends DataScopePerms> IPage<T> pagePerms(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper, @Param("data") D d);
    
    /**
     * 功能描述: 带权限的数量统计 <br/>
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     * @return "java.util.List<T>"
     */
    <D extends DataScopePerms> Long countPerms(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper, @Param("data") D d);
}

6.自定义IService与ServiceImpl

这两个可有可无。基本上都是直接调用Mapper的。留着以防万一嘛。

package com.hongtool.mybatis.perms.service;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.hongtool.mybatis.perms.domain.DataScopePerms;

import java.util.List;

/**
 * <p> 洪图 Mybatis-plus service扩展 </p>
 *
 * @author hongtool
 * @date 2022/11/8
 */
public interface IHongToolService<T> extends IService<T> {
    
    /**
     * 功能描述: 带权限的查询 <br/>
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     * @param d 数据权限
     * @return "java.util.List<T>"
     */
    <D extends DataScopePerms> List<T> listPerms(Wrapper<T> queryWrapper, D d);
    
    /**
     * 功能描述: 带权限的分页查询 <br/>
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     * @param d 数据权限
     * @return "java.util.List<T>"
     */
    <D extends DataScopePerms> IPage<T> pagePerms(IPage<T> page, Wrapper<T> queryWrapper, D d);
    
    /**
     * 功能描述: 带权限的数量统计 <br/>
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     * @param d 数据权限
     * @return long
     */
    <D extends DataScopePerms> Long countPerms(Wrapper<T> queryWrapper, D d);
}
package com.hongtool.mybatis.perms.service.impl;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hongtool.mybatis.perms.domain.DataScopePerms;
import com.hongtool.mybatis.perms.mapper.HongToolBaseMapper;
import com.hongtool.mybatis.perms.service.IHongToolService;

import java.util.List;

/**
 * <p> 洪图 Mybatis-plus serviceImpl扩展 </p>
 *
 * @author hongtool
 * @date 2022/11/8
 */
public class HongToolServiceImpl<M extends HongToolBaseMapper<T>, T> extends ServiceImpl<M, T> implements IHongToolService<T> {
    
    @Override
    public <D extends DataScopePerms> List<T> listPerms(Wrapper<T> wrapper, D d) {
        return baseMapper.listPerms(wrapper, d);
    }
    
    @Override
    public <D extends DataScopePerms> IPage<T> pagePerms(IPage<T> page, Wrapper<T> wrapper, D d) {
        return baseMapper.pagePerms(page, wrapper, d);
    }
    
    @Override
    public <D extends DataScopePerms> Long countPerms(Wrapper<T> wrapper, D d) {
        return baseMapper.countPerms(wrapper, d);
    }
    
}

7.切面处理(重点代码)

每个项目根据自己的实际情况,这里的写法都是不一样的。我这里是在登录的时候,将权限缓存到LoginUser中。并且使用了Shrio权限框架。所以就直接切RequiresPermissions注解,然后获取对应的权限类型,再对DataScopePerms赋值。

package com.hongtool.web.aspectj;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import com.hongtool.common.domain.LoginUser;
import com.hongtool.domain.module.system.enums.DataScopeEnum;
import com.hongtool.mybatis.perms.domain.DataScopePerms;
import com.hongtool.shiro.util.ShiroUtils;
import lombok.RequiredArgsConstructor;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * <p> 数据权限切面 </p>
 *
 * @author hongtool
 * @date 2022/11/1
 */
@Aspect
@Component
@RequiredArgsConstructor(onConstructor_ = {@Autowired})
public class DataScopeAspect {
    
    @Before(value = "@annotation(permissions)")
    public void doBefore(JoinPoint point, RequiresPermissions permissions) throws Throwable {
        if(ObjectUtil.isNull(point) || point.getArgs().length < 1) {
            return;
        }
        String[] value = permissions.value();
        Object params = point.getArgs()[0];
        if(ArrayUtil.isNotEmpty(value) && params instanceof DataScopePerms) {
            LoginUser loginUser = ShiroUtils.getLoginUser();
            List<LoginUser.Perms> permsList = loginUser.getPermsList();
            if(CollUtil.isNotEmpty(permsList)) {
                // 存在多个权限标识时,只取第一个(TODO 后续再优化)
                LoginUser.Perms perms = permsList.stream().filter(p -> p.getPerms().equals(value[0])).findFirst().orElse(null);
                if (perms != null) {
                    DataScopePerms dataScopePerms = (DataScopePerms) params;
                    if (DataScopeEnum.S5.getCode().equals(perms.getDataScope())) {
                        dataScopePerms.setUserPerm(loginUser.getLoginName());
                    } else {
                        dataScopePerms.setDeptPerms(perms.getDeptIds());
                    }
                }
            }
        }
    }

}

使用

1.添加依赖

<dependency>
    <groupId>com.hongtool</groupId>
    <artifactId>hongtool-mybatis-perms</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

2.入参继承DataScopePerms

import com.hongtool.mybatis.perms.domain.DataScopePerms;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * <p> 机构部门查询入参 </p>
 *
 * @author hongtool
 * @date 2022/10/21
 */
@EqualsAndHashCode(callSuper = true)
@Data
@ApiModel(value = "SysDeptQuery", description = "机构部门查询入参")
public class SysDeptQuery extends DataScopePerms {
    @ApiModelProperty("机构部门名称,模糊匹配")
    private String deptName;

    @ApiModelProperty("状态(0-启用;1-禁用)")
    private String status;
}

3.实体添加权限字段注解

package com.hongtool.domain.module.system.entity;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.hongtool.mybatis.perms.annotation.DeptPerm;
import com.hongtool.mybatis.perms.annotation.UserPerm;
import lombok.Data;

import java.time.LocalDateTime;

/**
 * <p> 机构部门表 </p>
 *
 * @author hongtool
 * @date 2022/9/23
 */
@Data
@TableName("sys_dept")
public class SysDeptEntity {
    
    @DeptPerm
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    /** 父节点id */
    @TableField("parent_id")
    private Long parentId;

    /** 祖级列表 前后都会加上"," 方便查找 */
    @TableField("ancestors")
    private String ancestors;

    /** 机构部门名称 */
    @TableField("dept_name")
    private String deptName;

    /** 机构部门类型(1-机构;2-部门) */
    @TableField("dept_type")
    private String deptType;

    /** 显示顺序 */
    @TableField("order_num")
    private Integer orderNum;

    /** 负责人 */
    @TableField("leader")
    private String leader;

    /** 联系电话 */
    @TableField("phone")
    private String phone;

    /** 邮箱 */
    @TableField("email")
    private String email;

    /** 状态(0-启用;1-禁用) */
    @TableField("status")
    private String status;

    /** 创建者 */
    @UserPerm
    @TableField("create_by")
    private String createBy;

    /** 更新者 */
    @TableField("update_by")
    private String updateBy;

    /** 备注 */
    @TableField("remark")
    private String remark;
    
    /** 创建时间 */
    @TableField(value = "create_time", fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    
    /** 修改时间 */
    @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
    
}

4.Mapper与Service继承自定义的Mapper与Service

省略ISysDeptService与SysDeptMapper代码

/**
 * <p> 机构部门 ServiceImpl </p>
 *
 * @author hongtool
 * @date 2022/9/23
 */
@Service
@RequiredArgsConstructor(onConstructor_ = {@Autowired})
public class SysDeptServiceImpl extends HongToolServiceImpl<SysDeptMapper, SysDeptEntity> implements ISysDeptService {
    
    private final ISysUserService sysUserService;
    
    @Override
    public List<SysDeptList> queryList(SysDeptQuery query, LoginUser loginUser) {
        // 权限查询(使用带权限的查询方法)
        List<SysDeptEntity> entityList = listPerms(
                Wrappers.<SysDeptEntity>lambdaQuery()
                .like(StrUtil.isNotEmpty(query.getDeptName()), SysDeptEntity::getDeptName, query.getDeptName())
                .eq(StrUtil.isNotEmpty(query.getStatus()), SysDeptEntity::getStatus, query.getStatus())
                .orderByAsc(SysDeptEntity::getOrderNum),
                query);
        return SysDeptConverter.INSTANCE.toSysDeptList(entityList);
    }
}

测试结果

机构部门:

==>  Preparing: SELECT id,parent_id,ancestors,dept_name,dept_type,order_num,leader,phone,email,status,create_by,update_by,remark,create_time,update_time FROM sys_dept WHERE id in ( ? , ? , ? ) ORDER BY order_num ASC
==> Parameters: 7(Long), 8(Long), 13(Long)

创建用户:

==>  Preparing: SELECT id,parent_id,ancestors,dept_name,dept_type,order_num,leader,phone,email,status,create_by,update_by,remark,create_time,update_time FROM sys_dept WHERE create_by = ? ORDER BY order_num ASC
==> Parameters: test2(String)

总结

数据权限还是非常复杂的内容,不同的设计有不同的实现方式。这里只是提供下其中的一种思路。欢迎各位大佬指正。

代码工程还不是很完善,就不开放了,如有需要的。视情况而定。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值