找回密码
 立即注册
首页 业界区 业界 Ruoyi框架 | 扩展部门数据权限实现

Ruoyi框架 | 扩展部门数据权限实现

司寇涵涵 昨天 21:00
一、背景与目标

在若依框架原有 DataScope 的基础上,实现一套独立的、基于部门层级的数据权限过滤机制,用于按组织结构灵活控制数据可见范围。
设计目标


  • 不依赖角色、不判断是否管理员
  • 通过注解参数动态控制数据范围
  • 支持:

    • 是否包含本部门
    • 向上查询 N 级部门
    • 向下查询 N 级部门 / 所有子部门

  • 与若依原有 BaseEntity + params + MyBatis XML 机制完全兼容
二、核心设计思路

1. 技术方案


  • 使用 AOP + 自定义注解 拦截查询方法
  • 在方法执行前:

    • 根据当前用户部门 ID
    • 动态拼接部门过滤 SQL
    • 注入到 BaseEntity.params.dataScope

  • Mapper XML 中通过 ${params.dataScope} 拼接 WHERE 条件
2. 依赖表结构(sys_dept)
  1. dept_id     部门ID
  2. parent_id   父部门ID
  3. ancestors   祖先路径,如:0,1,3,10
复制代码
三、自定义注解:ExtendedDataScope
  1. @Target(ElementType.METHOD)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface ExtendedDataScope {
  5.     // 部门表别名(必填)
  6.     String deptAlias() default "";
  7.     // 用户表别名(预留扩展)
  8.     String userAlias() default "";
  9.     // 权限类型(当前主要使用 dept)
  10.     String type() default "dept";
  11.     // 向上级部门层数(0 = 不包含)
  12.     int upLevel() default 0;
  13.     // 向下级部门层数(0 = 不包含,999 = 所有子级)
  14.     int downLevel() default 0;
  15.     // 是否包含本部门
  16.     boolean includeSelf() default true;
  17. }
复制代码
四、AOP 实现要点

1. 切面职责


  • 拦截所有标注 @ExtendedDataScope 的方法
  • 清空历史 dataScope,防止 SQL 注入
  • 生成部门层级 SQL
  • 写入 BaseEntity.params.dataScope
2. 核心处理流程
  1. @Before
  2.   ├─ clearDataScope()
  3.   ├─ 获取当前用户
  4.   ├─ 读取注解参数
  5.   ├─ 构建部门范围 SQL
  6.   └─ 写入 params.dataScope
复制代码
点击查看代码[code]@Aspect@Componentpublic class ExtendedDataScopeAspect {    @Autowired    private SysDeptMapper deptMapper;    /**     * 参数 key(和若依一致)     */    public static final String DATA_SCOPE = "dataScope";    @Before("@annotation(dataScope)")    public void doBefore(JoinPoint joinPoint, ExtendedDataScope dataScope) {        clearDataScope(joinPoint);        handleDataScope(joinPoint, dataScope);    }    private void handleDataScope(JoinPoint joinPoint, ExtendedDataScope dataScope) {        LoginUser loginUser = SecurityUtils.getLoginUser();        if (loginUser == null) {            return;        }        String deptAlias = dataScope.deptAlias();        if (StringUtils.isBlank(deptAlias)) {            return;        }        String sql = buildDeptScopeSql(loginUser.getDeptId(), deptAlias, dataScope);        if (StringUtils.isBlank(sql)) {            return;        }        Object params = joinPoint.getArgs()[0];        if (params instanceof BaseEntity) {            BaseEntity baseEntity = (BaseEntity) params;            baseEntity.getParams().put(DATA_SCOPE, " AND (" + sql + ")");        }    }    /**     * 构建部门层级 SQL     */    private String buildDeptScopeSql(Long deptId, String deptAlias, ExtendedDataScope scope) {        List conditions = new ArrayList();        /* ========== 本部门 ========== */        if (scope.includeSelf()) {            conditions.add(deptAlias + ".dept_id = " + deptId);        }        /* ========== 向上 ========== */        if (scope.upLevel() > 0) {            // ancestors 形如:0,1,3,10            // 向上 N 级:取 ancestors 中倒数 N 位            SysDept sysDept = deptMapper.selectDeptById(deptId);            String ancestors = sysDept.getAncestors();            if (ancestors != null) {                conditions.add(buildUpDeptSql(deptAlias, ancestors, scope.upLevel()));            }        }        /* ========== 向下 ========== */        if (scope.downLevel() > 0) {            if (scope.downLevel() >= 999) {                // 所有子级                conditions.add(                        deptAlias + ".dept_id IN (" +                                "SELECT dept_id FROM sys_dept " +                                "WHERE find_in_set(" + deptId + ", ancestors)" +                                ")"                );            } else {                conditions.add(buildDownDeptSql(deptAlias, deptId, scope.downLevel()));            }        }        return String.join(" OR ", conditions);    }    /**     * 向上 N 级部门     */    private String buildUpDeptSql(String deptAlias, String ancestors, int upLevel) {        // 使用子查询,取 ancestors 中的上级        return "find_in_set(" + deptAlias + ".dept_id," +                " SUBSTRING_INDEX('" + ancestors + "', ',', -" + upLevel + ") " +                ")";    }    /**     * 向下 N 级部门     */    private String buildDownDeptSql(String deptAlias, Long deptId, int downLevel) {        // ancestors 深度控制(当前 depth + N)        return deptAlias + ".dept_id IN (" +                " SELECT d.dept_id FROM sys_dept d " +                " WHERE find_in_set(" + deptId + ", d.ancestors) " +                " AND (LENGTH(d.ancestors) - LENGTH(REPLACE(d.ancestors, ',', '')))

相关推荐

您需要登录后才可以回帖 登录 | 立即注册