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();最佳实践
- 使用角色层级 - 为用户分配特定角色,而不是单独的权限
- 服务端检查权限 - 永远不要只信任客户端的权限检查
- 审计权限变更 - 记录所有策略修改
- 最小权限原则 - 从最小权限开始,按需添加