RuoYi-Vue-Plus数据范围:权限控制实现

RuoYi-Vue-Plus数据范围:权限控制实现

【免费下载链接】RuoYi-Vue-Plus 多租户后台管理系统 重写RuoYi-Vue所有功能 集成 Sa-Token、Mybatis-Plus、Warm-Flow工作流、SpringDoc、Hutool、OSS 定期同步 【免费下载链接】RuoYi-Vue-Plus 项目地址: https://gitcode.com/dromara/RuoYi-Vue-Plus

引言

在企业级应用开发中,数据权限控制是确保系统安全性的重要环节。RuoYi-Vue-Plus作为一款优秀的多租户后台管理系统,提供了完善的数据范围权限控制机制。本文将深入解析其数据权限实现原理、核心组件和使用方法,帮助开发者更好地理解和应用这一重要功能。

数据权限核心概念

数据范围类型

RuoYi-Vue-Plus定义了6种数据范围类型,每种类型对应不同的数据访问权限:

类型代码类型名称权限描述
1全部数据权限可访问所有数据
2自定数据权限自定义数据访问范围
3本部门数据权限仅能访问本部门数据
4本部门及以下数据权限可访问本部门及下级部门数据
5仅本人数据权限仅能访问本人创建的数据
6部门及以下或本人数据权限可访问本部门及下级部门数据或本人数据

核心注解体系

系统通过注解体系实现数据权限控制:

// 数据权限组注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataPermission {
    DataColumn[] value();
    String joinStr() default "";
}

// 数据列注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataColumn {
    String[] key() default "deptName";
    String[] value() default "dept_id";
    String permission() default "";
}

实现原理深度解析

数据权限处理流程

mermaid

核心枚举类:DataScopeType

@Getter
@AllArgsConstructor
public enum DataScopeType {
    ALL("1", "", ""),  // 全部数据权限
    CUSTOM("2", " #{#deptName} IN ( #{@sdss.getRoleCustom( #user.roleId )} ) ", " 1 = 0 "),
    DEPT("3", " #{#deptName} = #{#user.deptId} ", " 1 = 0 "),
    DEPT_AND_CHILD("4", " #{#deptName} IN ( #{@sdss.getDeptAndChild( #user.deptId )} )", " 1 = 0 "),
    SELF("5", " #{#userName} = #{#user.userId} ", " 1 = 0 "),
    DEPT_AND_CHILD_OR_SELF("6", 
        " #{#deptName} IN ( #{@sdss.getDeptAndChild( #user.deptId )} ) OR #{#userName} = #{#user.userId} ", 
        " 1 = 0 ");
    
    private final String code;
    private final String sqlTemplate;  // SpEL模板表达式
    private final String elseSql;      // 默认SQL表达式
}

数据权限拦截器机制

系统通过PlusDataPermissionInterceptor拦截器实现SQL重写:

public class PlusDataPermissionInterceptor implements Interceptor {
    
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 检查是否需要忽略数据权限处理
        if (DataPermissionHelper.isIgnore()) {
            return invocation.proceed();
        }
        
        // 检查方法上的数据权限注解
        MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
        DataPermission dataPermission = getDataPermission(ms);
        
        if (dataPermission == null) {
            return invocation.proceed();
        }
        
        // 处理数据权限逻辑
        return processDataPermission(invocation, dataPermission);
    }
}

实战应用示例

用户数据权限配置

@DataPermission({
    @DataColumn(key = "deptName", value = "dept_id"),
    @DataColumn(key = "userName", value = "user_id")
})
default Page<SysUserVo> selectPageUserList(Page<SysUser> page, Wrapper<SysUser> queryWrapper) {
    return this.selectVoPage(page, queryWrapper);
}

部门数据权限配置

@DataPermission({
    @DataColumn(key = "deptName", value = "dept_id")
})
List<SysDept> selectDeptList(@Param(Constants.WRAPPER) Wrapper<SysDept> queryWrapper);

自定义数据权限处理

对于自定义数据权限类型(类型2),系统会调用数据权限服务:

public class SysDataScopeServiceImpl implements ISysDataScopeService {
    
    public List<Long> getRoleCustom(Long roleId) {
        // 查询角色自定义的数据权限部门列表
        return baseMapper.selectCustomDeptIdList(roleId);
    }
    
    public List<Long> getDeptAndChild(Long deptId) {
        // 查询部门及所有下级部门ID列表
        return baseMapper.selectDeptAndChildIdList(deptId);
    }
}

权限校验机制

用户数据权限校验

public void checkUserDataScope(Long userId) {
    if (!UserUtils.isAdmin(SecurityUtils.getUserId())) {
        SysUser user = baseMapper.selectById(userId);
        if (StringUtils.isNull(user)) {
            throw new ServiceException("没有权限访问用户数据!");
        }
        // 检查当前用户是否有权限访问目标用户数据
        if (!hasUserDataScope(user)) {
            throw new ServiceException("没有权限访问用户数据!");
        }
    }
}

角色数据权限校验

public void checkRoleDataScope(Long roleId) {
    if (!UserUtils.isAdmin(SecurityUtils.getUserId())) {
        SysRole role = baseMapper.selectById(roleId);
        if (StringUtils.isNull(role)) {
            throw new ServiceException("没有权限访问角色数据!");
        }
        // 检查当前用户是否有权限访问目标角色数据
        if (!hasRoleDataScope(role)) {
            throw new ServiceException("没有权限访问角色数据!");
        }
    }
}

高级特性

忽略数据权限

在某些场景下需要临时忽略数据权限检查:

// 开启忽略数据权限
DataPermissionHelper.enableIgnore();

try {
    // 执行需要忽略权限的SQL操作
    userMapper.selectList(null);
} finally {
    // 关闭忽略数据权限
    DataPermissionHelper.disableIgnore();
}

权限表达式语言(SpEL)支持

系统支持SpEL表达式,提供灵活的权限控制:

// SpEL表达式示例
" #{#deptName} IN ( #{@sdss.getDeptAndChild( #user.deptId )} ) OR #{#userName} = #{#user.userId} "

内置变量:

  • #user: 当前登录用户信息
  • #deptName: 部门字段占位符
  • #userName: 用户字段占位符
  • @sdss: 系统数据权限服务

最佳实践

1. 合理设计数据权限策略

mermaid

2. 性能优化建议

  • 缓存机制: 对频繁查询的数据权限结果进行缓存
  • 索引优化: 确保权限相关字段建立合适索引
  • 批量处理: 避免在循环中进行权限校验

3. 安全注意事项

  • 始终在服务层进行权限校验,不依赖前端校验
  • 定期审计数据权限配置
  • 实现细粒度的权限控制,避免过度授权

常见问题解决方案

问题1:数据权限不生效

解决方案

  1. 检查方法是否添加了@DataPermission注解
  2. 确认用户角色是否正确配置数据范围
  3. 验证SpEL表达式语法是否正确

问题2:权限校验性能问题

优化方案

// 使用缓存优化权限查询
@Cacheable(value = "userDataScope", key = "#userId")
public boolean hasUserDataScope(Long userId) {
    // 权限校验逻辑
}

问题3:复杂业务场景权限控制

扩展方案

// 自定义数据权限处理器
public class CustomDataPermissionHandler implements DataPermissionHandler {
    
    @Override
    public String process(String originalSql, DataPermission dataPermission) {
        // 自定义权限处理逻辑
        return buildCustomSqlCondition(originalSql, dataPermission);
    }
}

总结

RuoYi-Vue-Plus的数据权限控制机制通过注解驱动、拦截器处理和SpEL表达式等技术,实现了灵活而强大的数据范围控制。系统提供了6种标准数据范围类型,支持自定义扩展,能够满足大多数企业级应用的权限控制需求。

关键优势:

  • 注解驱动: 通过简单的注解配置即可实现复杂权限控制
  • 灵活扩展: 支持自定义数据权限类型和处理逻辑
  • 性能优化: 内置缓存和优化机制,确保系统性能
  • 安全可靠: 多层次权限校验,确保数据安全

通过深入理解和合理应用这一机制,开发者可以构建出既安全又高效的企业级应用系统。

【免费下载链接】RuoYi-Vue-Plus 多租户后台管理系统 重写RuoYi-Vue所有功能 集成 Sa-Token、Mybatis-Plus、Warm-Flow工作流、SpringDoc、Hutool、OSS 定期同步 【免费下载链接】RuoYi-Vue-Plus 项目地址: https://gitcode.com/dromara/RuoYi-Vue-Plus

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值