授权(RBAC)

XinAdmin 实现了标准的用户-角色-权限三层 RBAC 授权模型。

RBAC 模型

┌──────────┐    多对多    ┌──────────┐    多对多    ┌──────────────┐
│  SysUser │ <──────────> │ SysRole  │ <──────────> │   SysRule    │
│  (用户)   │ sys_user_role│ (角色)   │sys_role_rule│ (权限规则)    │
└──────────┘              └──────────┘              └──────────────┘

数据表结构

说明
sys_user系统用户(id, username, password, status 等)
sys_role角色(id, name, sort, status)
sys_user_role用户-角色关联表
sys_rule权限规则(id, key, type, name, path, icon, status 等)
sys_role_rule角色-权限关联表

权限规则类型

type说明示例 key
menu菜单分组,用于构建导航树system
route路由/页面,对应可访问的页面system.user
rule操作权限,控制页面内的具体操作system.user.createsystem.user.updatesystem.user.delete

权限获取逻辑

// SysUserModel::access() - 获取用户的所有权限 key
public function access()
{
    // 超级管理员(ID=1):返回所有权限
    if ($this->id === 1) {
        return SysRuleModel::pluck('key')->toArray();
    }
    // 普通用户:通过角色-权限关联获取
    return $this->roles()
        ->with('rules')
        ->get()
        ->pluck('rules.*.key')
        ->flatten()
        ->unique()
        ->toArray();
}

超级管理员

用户 ID = 1 被系统硬编码为超级管理员,具备以下特权:

  • 自动获得所有权限 key
  • 在 Token 能力检查中始终通过
  • 不可被删除
  • 自动获取完整菜单树
  • 角色 ID = 1 的权限不可被修改

超级管理员的特权在 三个层面 实现:SysUserModel::access() 返回全部 key、SysRoleModel::getRuleIdsAttribute() 返回全部规则、SysAccessToken::can() 始终返回 true。

动态菜单

用户登录后,后端根据其角色权限返回对应的菜单树:

  • 超级管理员:直接返回所有 status=1 的菜单/路由
  • 普通用户:通过角色 → 权限关联链获取菜单,构建树形结构并去重

前端 LayoutContext 在挂载时调用 menu() API,MenuRender 根据菜单类型和隐藏状态渲染导航。

前端权限控制

AuthButton 组件

基于权限 key 的条件渲染组件:

<AuthButton auth="system.user.create">
  <Button type="primary">新增用户</Button>
</AuthButton>

无权限时组件返回 null,按钮不会被渲染。实现原理:

// AuthButton 核心逻辑
const access = useAuthStore(state => state.access);

if (!auth) return <>{children}</>;
if (!access.includes(auth)) return null;
return <>{children}</>;

useAuth Hook

在逻辑代码中手动检查权限:

import useAuth from '@/hooks/useAuth';

function MyComponent() {
  const { auth } = useAuth();

  return (
    <>
      {/* 有权限时才显示 */}
      {auth('system.user.delete') && <Button danger>删除</Button>}

      {/* 有权限时才启用控件 */}
      <Switch disabled={!auth('system.user.update')} />
    </>
  );
}

XinTable 的 accessName

XinTable 组件的 accessName 属性自动为操作按钮生成权限 key:

<XinTable accessName="system.user" ... />

// 自动生成以下权限控制:
//   新增按钮 → system.user.create
//   编辑按钮 → system.user.update
//   删除按钮 → system.user.delete

权限 Key 命名规范

推荐使用点号分隔的层级命名:

{模块}.{子模块}.{操作}

示例:
  system.user.query      # 系统管理 → 用户 → 查询
  system.user.create     # 系统管理 → 用户 → 新增
  system.role.update     # 系统管理 → 角色 → 编辑
  file.manage.delete     # 文件管理 → 删除