后台权限与角色

RBAC 配置指南

角色权限配置详细指南

基础概念 · 配置步骤 · 最佳实践


🎯 概述

RBAC(Role-Based Access Control)是 NextJS Base 的权限管理系统,通过角色来管理用户的访问权限。

核心概念

用户 (User)

   └─── 拥有角色 ───► 角色 (Role)

         ┌───────────────┼───────────────┐
         │               │               │
         ▼               ▼               ▼
      权限           菜单           继承菜单权限
   (Permission)     (Menu)     (inheritMenuPermissions)
         │               │
         ▼               ▼
    Server Actions   页面访问

📚 基础概念

权限 (Permission)

权限定义了用户可以执行的操作:

字段说明示例
name权限名称查看文章、创建文章
parentId父级 ID(支持树形)null / 父权限 ID
crudCategoryCRUD 分类post、user、role
actions关联的 Server Actions['sysGetPostList', 'sysCreatePost']
apis关联的 API 路径['/api/v1/posts']

菜单 (Menu)

菜单定义了后台侧边栏的显示:

字段说明示例
name菜单名称文章管理
parentId父级 ID(支持树形)null / 父菜单 ID
url页面路径/admin/content/posts
icon图标FileTextOutlined
permission关联的权限 ID 数组['perm_id_1', 'perm_id_2']

角色 (Role)

角色是权限和菜单的集合:

字段说明示例
name角色名称admin、editor
permission权限 ID 数组['perm_id_1', 'perm_id_2']
menu菜单 ID 数组['menu_id_1', 'menu_id_2']
inheritMenuPermissions是否继承菜单权限true / false

📝 配置步骤

Step 1: 创建权限

在「权限管理」页面创建权限:

文章管理(父级权限)
├── 查看文章
│   └── actions: ['sysGetPostList', 'sysGetPostDetail']
├── 创建文章
│   └── actions: ['sysCreatePost']
├── 编辑文章
│   └── actions: ['sysUpdatePost']
└── 删除文章
    └── actions: ['sysDeletePost', 'sysBatchDeletePost']

配置示例

字段父级权限子权限(查看文章)
名称文章管理查看文章
父级-文章管理
CRUD 分类postpost
Actions[]['sysGetPostList', 'sysGetPostDetail']

Step 2: 创建菜单

在「菜单管理」页面创建菜单:

内容管理(父级菜单)
├── 文章管理
│   ├── URL: /admin/content/posts
│   ├── 图标: FileTextOutlined
│   └── 关联权限: [查看文章, 创建文章, 编辑文章, 删除文章]
└── 分类管理
    ├── URL: /admin/content/categories
    └── 关联权限: [查看分类, 创建分类, ...]

Step 3: 创建角色

在「角色管理」页面创建角色:

管理员角色

字段
名称admin
权限[] (可为空)
菜单[所有菜单]
继承菜单权限✅ 是

编辑角色

字段
名称editor
权限[查看文章, 创建文章, 编辑文章]
菜单[文章管理]
继承菜单权限❌ 否

Step 4: 分配角色给用户

在「用户管理」页面为用户分配角色:

  1. 找到目标用户
  2. 点击「编辑」
  3. 在「角色」字段选择角色
  4. 确保「后台访问」已开启
  5. 保存

🔐 权限检查流程

Action 权限检查

// 1. 调用 Server Action
sysGetPostListAction()

// 2. wrapAction 解析 Action 名称
//    - 前缀: sys → 需要后台权限
//    - 操作: GetPostList

// 3. 检查用户是否有后台访问权限
if (!user.hasBackendAccess) {
  throw new Error('无后台访问权限')
}

// 4. 获取用户角色的所有权限
const permissions = await getUserPermissions(user.roles)

// 5. 检查 Action 是否在权限列表中
const allowedActions = permissions.flatMap(p => p.actions)
if (!allowedActions.includes('sysGetPostList')) {
  throw new Error('无权执行此操作')
}

// 6. 执行业务逻辑

权限聚合与菜单继承

  • 用户权限 = 角色上显式配置的 permission 并集 所有「继承菜单权限」角色的菜单关联权限。实现位置:app/(admin)/actions/dao/sys.js#getUserPermissionIds
  • 继承逻辑:若角色 inheritMenuPermissions === true,则把该角色的 menu 中每个菜单的 permission 一起加入,最后去重。
  • Action 匹配:权限里的 actions(支持 sysCreateExample* 这类通配符)由 checkUserHasActionPermission 取出并匹配当前 Server Action。
  • 例:菜单“with permissions”关联权限“example write(no delete)”,该权限的 actions 包含 sysCreateExample*/sysUpdateExample*/sysBatchUpdateExample*/sysActivateExample*/sysDeactivateExample*。把此菜单授予角色并勾选「Inherit Menu Permissions」后,再把角色授予用户,用户最终就拥有上述动作权限。

菜单权限检查

// 1. 获取用户角色
const roles = await getRoles(user.roles)

// 2. 汇总所有菜单 ID
const menuIds = roles.flatMap(r => r.menu)

// 3. 获取菜单详情
const menus = await getMenus(menuIds)

// 4. 过滤启用且未隐藏的菜单
const visibleMenus = menus.filter(m => m.enable && !m.hidden)

// 5. 构建菜单树并渲染侧边栏

🎨 配置示例

示例:电商系统权限

商品管理
├── 查看商品 [sysGetProductList, sysGetProductDetail]
├── 创建商品 [sysCreateProduct]
├── 编辑商品 [sysUpdateProduct]
├── 删除商品 [sysDeleteProduct]
└── 上下架商品 [sysToggleProductStatus]

订单管理
├── 查看订单 [sysGetOrderList, sysGetOrderDetail]
├── 处理订单 [sysUpdateOrder]
├── 取消订单 [sysCancelOrder]
└── 导出订单 [sysExportOrder]

用户管理
├── 查看用户 [sysGetUserList, sysGetUserDetail]
├── 编辑用户 [sysUpdateUser]
├── 封禁用户 [sysBanUser]
└── 重置密码 [sysResetUserPassword]

示例:角色配置

超级管理员

{
  name: 'super_admin',
  permission: [],  // 空,使用菜单继承
  menu: ['所有菜单 ID'],
  inheritMenuPermissions: true,
}

运营人员

{
  name: 'operator',
  permission: [
    '查看商品', '编辑商品', '上下架商品',
    '查看订单', '处理订单',
  ],
  menu: ['商品管理', '订单管理'],
  inheritMenuPermissions: false,
}

客服人员

{
  name: 'customer_service',
  permission: [
    '查看订单',
    '查看用户',
  ],
  menu: ['订单管理', '用户管理'],
  inheritMenuPermissions: false,
}

✅ 最佳实践

1. 权限粒度

✅ 推荐:细粒度权限
├── 查看文章
├── 创建文章
├── 编辑文章
└── 删除文章

❌ 不推荐:粗粒度权限
└── 文章管理(包含所有操作)

2. 使用 CRUD 分类

// ✅ 推荐:使用 crudCategory 分组
{
  name: '查看文章',
  crudCategory: 'post',  // 便于筛选和管理
  actions: ['sysGetPostList'],
}

3. 菜单权限继承

// ✅ 管理员:使用继承简化配置
{
  name: 'admin',
  menu: ['所有菜单'],
  inheritMenuPermissions: true,  // 自动获得菜单关联的权限
}

// ✅ 普通角色:显式分配权限
{
  name: 'editor',
  permission: ['具体权限'],
  menu: ['编辑相关菜单'],
  inheritMenuPermissions: false,  // 精确控制权限
}

4. 权限命名规范

{模块名称} - {操作}

示例:
- 文章管理 - 查看
- 文章管理 - 创建
- 文章管理 - 编辑
- 文章管理 - 删除

5. 定期审计

  • 定期检查角色权限配置
  • 移除不再使用的权限
  • 检查用户角色分配是否合理
  • 查看操作日志发现异常

📚 相关文档