RuoYi-Vue多租户设计:SaaS平台权限隔离方案
在SaaS(软件即服务)平台开发中,多租户(Multi-Tenancy)架构是实现资源共享与数据隔离的关键设计。本文将基于RuoYi-Vue框架,从权限隔离角度探讨如何通过现有功能实现多租户数据隔离,为企业级SaaS平台提供可落地的解决方案。
多租户隔离的核心挑战
多租户架构要求在共享系统资源的同时,确保不同租户数据的安全性与独立性。典型挑战包括:
- 数据隔离:租户数据逻辑/物理分离
- 权限控制:细粒度的租户内权限管理
- 配置隔离:租户个性化配置独立存储
RuoYi-Vue作为成熟的权限管理系统,虽未直接提供多租户模块,但通过其数据权限框架和RBAC(基于角色的访问控制)模型,可实现轻量级多租户隔离。
基于数据权限的租户隔离方案
数据权限控制原理
RuoYi-Vue的数据权限控制通过@DataScope注解实现,在SQL查询时动态添加数据过滤条件。核心实现位于:
// 角色数据权限检查
@Service
public class SysRoleServiceImpl implements ISysRoleService {
@Override
public void checkRoleDataScope(Long... roleIds) {
if (!SysUser.isAdmin(SecurityUtils.getUserId())) {
for (Long roleId : roleIds) {
SysRole role = new SysRole();
role.setRoleId(roleId);
List<SysRole> roles = SpringUtils.getAopProxy(this).selectRoleList(role);
if (StringUtils.isEmpty(roles)) {
throw new ServiceException("没有权限访问角色数据!");
}
}
}
}
}
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java
租户隔离实现方案
1. 租户标识设计
在用户表sys_user中添加租户ID字段tenant_id,并通过数据权限控制实现租户数据隔离:
ALTER TABLE sys_user ADD COLUMN tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID';
2. 数据权限扩展
修改数据权限注解,添加租户维度过滤:
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataScope {
String tenantAlias() default ""; // 租户别名
String deptAlias() default ""; // 部门别名
String userAlias() default ""; // 用户别名
}
3. SQL拦截器增强
在数据权限拦截器中添加租户过滤条件:
public class DataScopeInterceptor implements MethodInterceptor {
private void handleDataScope(SysUser user, StringBuilder sqlString, String tenantAlias) {
if (user.getTenantId() != 0 && StringUtils.isNotBlank(tenantAlias)) {
sqlString.append(" AND ").append(tenantAlias).append(".tenant_id = ").append(user.getTenantId());
}
}
}
租户内RBAC权限模型
租户角色体系设计
利用RuoYi-Vue的角色管理功能,为每个租户创建独立的角色体系:
- 系统管理员:管理租户及全局配置
- 租户管理员:管理租户内用户与权限
- 租户用户:按角色分配具体操作权限
角色管理界面提供了完整的角色创建、权限分配功能:
租户菜单权限控制
通过菜单权限配置,实现租户功能模块的差异化:
// 菜单权限过滤实现
@Service
public class SysMenuServiceImpl implements ISysMenuService {
@Override
public List<SysMenu> selectMenuList(SysMenu menu, Long userId) {
List<SysMenu> menuList = null;
// 系统管理员显示所有菜单
if (SysUser.isAdmin(userId)) {
menuList = menuMapper.selectMenuList(menu);
} else {
menu.getParams().put("userId", userId);
menuList = menuMapper.selectMenuListByUserId(menu);
}
return menuList;
}
}
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java
数据隔离实现步骤
1. 扩展用户表结构
添加租户ID字段,区分不同租户用户:
-- 示例SQL脚本
ALTER TABLE sys_user ADD COLUMN tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID';
ALTER TABLE sys_role ADD COLUMN tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户ID';
2. 修改数据权限注解
// 在角色查询方法添加租户过滤
@DataScope(deptAlias = "d", userAlias = "u", tenantAlias = "t")
public List<SysRole> selectRoleList(SysRole role) {
return roleMapper.selectRoleList(role);
}
3. 配置租户数据权限
在角色管理界面配置租户数据权限范围:
通过"数据权限"功能,可精确控制租户能访问的数据范围:
- 全部数据
- 自定义数据
- 本部门数据
- 本部门及以下数据
- 仅本人数据
方案优势与局限
优势
- 零侵入扩展:基于现有权限框架,无需重构核心代码
- 细粒度控制:结合RBAC模型实现租户内权限精细化管理
- 低成本实现:复用框架成熟功能,降低开发与维护成本
局限
- 共享数据库实例:无法实现物理隔离,存在资源竞争风险
- 租户数量限制:大量租户可能导致性能下降
- 自定义受限:复杂租户需求需额外开发
进阶扩展建议
对于有更高隔离需求的场景,可考虑以下扩展方向:
- 独立Schema方案:为每个租户创建独立数据库Schema
- 动态数据源路由:基于租户ID动态切换数据源
- 多租户插件集成:集成MyBatis-Plus多租户插件
核心实现可参考:
// 动态数据源路由示例
public class TenantDataSourceRouter extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return TenantContextHolder.getTenantId();
}
}
总结
RuoYi-Vue框架通过其灵活的数据权限和RBAC模型,为实现多租户隔离提供了坚实基础。本文介绍的方案利用现有功能,以最小成本实现了租户数据隔离,适合中小型SaaS平台使用。
官方文档:doc/若依环境使用手册.docx
完整实现需结合业务场景进行调整,建议优先采用数据权限控制方案,在租户规模增长后再考虑更复杂的物理隔离方案。
相关模块路径:
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考





