Docs/modules/RBAC(基于角色的访问控制)

RBAC(基于角色的访问控制)

NextShip 使用 Casbin 实现灵活的、基于策略的访问控制。

功能特性

  • 角色层级(superadmin > admin > user)
  • 动态策略管理
  • 权限配置管理界面
  • 数据库存储策略

角色层级

superadmin
    └── admin
          └── user
角色描述
superadmin完全系统访问权限,可管理其他管理员
admin管理员仪表板访问权限,用户管理
user基本访问自己的资源

角色继承下级角色的权限。管理员拥有所有用户权限,外加管理员特定的权限。

工作原理

策略模型

Casbin 使用 PERM 模型(策略、效果、请求、匹配器):

# config/rbac_model.conf
[request_definition]
r = sub, obj, act
 
[policy_definition]
p = sub, obj, act
 
[role_definition]
g = _, _
 
[policy_effect]
e = some(where (p.eft == allow))
 
[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
  • sub: 主体(用户 ID 或角色)
  • obj: 对象(资源,如 "users"、"payments")
  • act: 动作("read"、"write"、"delete")

默认策略

默认策略定义在 config/rbac-defaults.json

{
  "policies": [
    ["admin", "admin", "access"],
    ["admin", "users", "read"],
    ["admin", "users", "write"],
    ["admin", "audit-logs", "read"],
    ["admin", "payments", "read"],
    ["admin", "emails", "read"],
    ["user", "dashboard", "access"],
    ["user", "settings", "read"],
    ["user", "settings", "write"]
  ],
  "roleHierarchy": [
    ["superadmin", "admin"],
    ["admin", "user"]
  ]
}

Server Actions

权限检查

// src/server/actions/permission.ts
 
// 检查当前用户是否具有权限
export async function checkUserPermission(object: string, action: string) {
  const session = await requireAuth();
  const enforcer = await getEnforcer();
  return enforcer.enforce(session.user.id, object, action);
}
 
// 检查当前用户是否具有角色
export async function checkUserRole(role: string) {
  const session = await requireAuth();
  const enforcer = await getEnforcer();
  return enforcer.hasRoleForUser(session.user.id, role);
}
 
// 获取当前用户的角色
export async function getMyRoles() {
  const session = await requireAuth();
  const enforcer = await getEnforcer();
  return enforcer.getRolesForUser(session.user.id);
}
 
// 获取当前用户的权限
export async function getMyPermissions() {
  const session = await requireAuth();
  const enforcer = await getEnforcer();
  return enforcer.getPermissionsForUser(session.user.id);
}

管理员操作

// 为用户分配角色
export async function assignUserRole(userId: string, role: string) {
  await requireAdmin();
  const enforcer = await getEnforcer();
  await enforcer.addRoleForUser(userId, role);
  await enforcer.savePolicy();
}
 
// 移除用户角色
export async function removeUserRole(userId: string, role: string) {
  await requireAdmin();
  const enforcer = await getEnforcer();
  await enforcer.deleteRoleForUser(userId, role);
  await enforcer.savePolicy();
}
 
// 添加权限策略
export async function addPermissionPolicy(subject: string, object: string, action: string) {
  await requireAdmin();
  const enforcer = await getEnforcer();
  await enforcer.addPolicy(subject, object, action);
  await enforcer.savePolicy();
}
 
// 移除权限策略
export async function removePermissionPolicy(subject: string, object: string, action: string) {
  await requireAdmin();
  const enforcer = await getEnforcer();
  await enforcer.removePolicy(subject, object, action);
  await enforcer.savePolicy();
}

使用示例

保护页面

// src/app/[locale]/(dashboard)/users/page.tsx
import { requireAdmin } from "@/lib/permissions";
 
export default async function UsersPage() {
  await requireAdmin();
  // 只有管理员可以访问这里
}

保护 Server Actions

// src/server/actions/admin.ts
import { requirePermission } from "@/lib/permissions";
 
export async function deleteUser(userId: string) {
  await requirePermission("users", "delete");
  // 只有具有 "users:delete" 权限的用户可以执行
}

客户端角色检查

"use client";
 
import { usePermissions } from "@/hooks/use-permissions";
 
export function AdminButton() {
  const { hasRole, isLoading } = usePermissions();
 
  if (isLoading) return null;
  if (!hasRole("admin")) return null;
 
  return <Button>管理员操作</Button>;
}

管理员仪表板

权限页面(/permissions)允许管理员:

  • 查看所有策略
  • 添加/移除策略
  • 为用户分配/移除角色
  • 查看用户权限

数据库存储

策略存储在 casbin_rule 表中:

// src/lib/db/schema.ts
export const casbinRule = pgTable("casbin_rule", {
  id: serial("id").primaryKey(),
  ptype: text("ptype").notNull(),
  v0: text("v0"),
  v1: text("v1"),
  v2: text("v2"),
  v3: text("v3"),
  v4: text("v4"),
  v5: text("v5"),
});

初始化默认策略

首次运行或重置时,初始化默认策略:

import { initializeDefaultPolicies } from "@/server/actions/permission";
 
// 在管理员设置或种子脚本中
await initializeDefaultPolicies();

最佳实践

  1. 使用角色层级 - 为用户分配特定角色,而不是单独的权限
  2. 服务端检查权限 - 永远不要只信任客户端的权限检查
  3. 审计权限变更 - 记录所有策略修改
  4. 最小权限原则 - 从最小权限开始,按需添加