Bknd权限分配Skill bknd-assign-permissions

这个技能用于在Bknd框架中配置角色的详细权限,支持简单字符串、扩展格式(允许/拒绝效果)、条件策略、实体特定权限和细粒度访问控制模式,帮助开发者实现安全的后端访问控制。关键词:Bknd、权限配置、角色管理、访问控制、条件策略、后端安全、代码优先权限。

后端开发 0 次安装 0 次浏览 更新于 3/9/2026

名称: bknd-assign-permissions 描述: 在Bknd中为角色分配权限时使用。涵盖权限语法(简单字符串、扩展格式)、权限效果(允许/拒绝)、带条件的策略、实体特定权限和细粒度访问控制模式。

分配权限

使用简单字符串、带效果的扩展格式和条件策略为角色配置详细权限。

先决条件

  • 具有代码优先配置的Bknd项目
  • 认证已启用(auth: { enabled: true }
  • 守卫已启用(guard: { enabled: true }
  • 至少定义了一个角色(参见 bknd-create-role

何时使用UI模式

  • 查看当前角色权限
  • 快速权限检查

UI步骤: 管理面板 > 认证 > 角色 > 选择角色

注意: 权限分配需要代码模式。UI是只读的。

何时使用代码模式

  • 为角色分配权限
  • 添加权限效果(允许/拒绝)
  • 创建条件策略
  • 实体特定权限规则

代码方法

步骤1:简单权限字符串

将基本权限分配为字符串数组:

import { serve } from "bknd/adapter/bun";
import { em, entity, text } from "bknd";

const schema = em({
  posts: entity("posts", { title: text().required() }),
});

serve({
  connection: { url: "file:data.db" },
  config: {
    data: schema.toJSON(),
    auth: {
      enabled: true,
      guard: { enabled: true },
      roles: {
        editor: {
          implicit_allow: false,
          permissions: [
            "data.entity.read",    // 读取任何实体
            "data.entity.create",  // 在任何实体中创建
            "data.entity.update",  // 更新任何实体
            // 无删除权限
          ],
        },
      },
    },
  },
});

可用权限

权限 可过滤 描述
data.entity.read 读取实体记录
data.entity.create 创建新记录
data.entity.update 更新现有记录
data.entity.delete 删除记录
data.database.sync 同步数据库架构
data.raw.query 执行原始SELECT
data.raw.mutate 执行原始INSERT/UPDATE/DELETE

可过滤 意味着您可以通过策略添加条件/过滤器。

步骤2:扩展权限格式

使用对象实现明确的允许/拒绝效果:

{
  roles: {
    moderator: {
      implicit_allow: false,
      permissions: [
        { permission: "data.entity.read", effect: "allow" },
        { permission: "data.entity.update", effect: "allow" },
        { permission: "data.entity.delete", effect: "deny" },  // 明确拒绝
      ],
    },
  },
}

权限效果

效果 描述
allow 授予权限(默认)
deny 明确阻止权限

拒绝覆盖允许 - 当 implicit_allow: true 但您想阻止特定操作时有用。

步骤3:条件策略

添加策略以实现细粒度控制:

{
  roles: {
    content_editor: {
      implicit_allow: false,
      permissions: [
        {
          permission: "data.entity.read",
          effect: "allow",
          policies: [
            {
              description: "仅读取帖子和评论",
              condition: { entity: { $in: ["posts", "comments"] } },
              effect: "allow",
            },
          ],
        },
        {
          permission: "data.entity.create",
          effect: "allow",
          policies: [
            {
              condition: { entity: { $in: ["posts", "comments"] } },
              effect: "allow",
            },
          ],
        },
      ],
    },
  },
}

策略结构

{
  description?: string,      // 人类可读(可选)
  condition?: ObjectQuery,   // 策略应用的条件
  effect: "allow" | "deny" | "filter",
  filter?: ObjectQuery,      // 行过滤器(适用于 effect: "filter")
}

策略效果

效果 目的
allow 条件满足时授予
deny 条件满足时阻止
filter 对结果应用行级过滤器

条件运算符

运算符 描述 示例
$eq 等于 { entity: { $eq: "posts" } }
$ne 不等于 { entity: { $ne: "users" } }
$in 在数组中 { entity: { $in: ["posts", "comments"] } }
$nin 不在数组中 { entity: { $nin: ["users", "secrets"] } }
$gt 大于 { age: { $gt: 18 } }
$gte 大于或等于 { level: { $gte: 5 } }
$lt 小于 { count: { $lt: 100 } }
$lte 小于或等于 { priority: { $lte: 3 } }

步骤4:变量占位符

使用 @variable 引用运行时上下文:

占位符 描述
@user.id 当前用户的ID
@user.email 当前用户的电子邮件
@user.role 当前用户的角色
@entity 当前实体名称
@id 当前记录ID

示例 - 用户只能更新自己的个人资料:

{
  permissions: [
    {
      permission: "data.entity.update",
      effect: "allow",
      policies: [
        {
          condition: { entity: "users", "@id": "@user.id" },
          effect: "allow",
        },
      ],
    },
  ],
}

步骤5:实体特定权限

为每个实体授予不同的权限:

{
  roles: {
    blog_author: {
      implicit_allow: false,
      permissions: [
        // 对帖子的完整CRUD
        {
          permission: "data.entity.read",
          effect: "allow",
          policies: [{ condition: { entity: "posts" }, effect: "allow" }],
        },
        {
          permission: "data.entity.create",
          effect: "allow",
          policies: [{ condition: { entity: "posts" }, effect: "allow" }],
        },
        {
          permission: "data.entity.update",
          effect: "allow",
          policies: [{ condition: { entity: "posts" }, effect: "allow" }],
        },
        {
          permission: "data.entity.delete",
          effect: "allow",
          policies: [{ condition: { entity: "posts" }, effect: "allow" }],
        },

        // 对类别的只读权限
        {
          permission: "data.entity.read",
          effect: "allow",
          policies: [{ condition: { entity: "categories" }, effect: "allow" }],
        },
      ],
    },
  },
}

常见模式

只读角色

{
  roles: {
    viewer: {
      implicit_allow: false,
      permissions: ["data.entity.read"],
    },
  },
}

无删除的CRUD

{
  roles: {
    contributor: {
      implicit_allow: false,
      permissions: [
        "data.entity.read",
        "data.entity.create",
        "data.entity.update",
        { permission: "data.entity.delete", effect: "deny" },
      ],
    },
  },
}

具有受限原始访问权限的管理员

{
  roles: {
    admin: {
      implicit_allow: true,  // 默认允许所有
      permissions: [
        // 但拒绝原始数据库访问
        { permission: "data.raw.query", effect: "deny" },
        { permission: "data.raw.mutate", effect: "deny" },
      ],
    },
  },
}

多实体角色

{
  roles: {
    content_manager: {
      implicit_allow: false,
      permissions: [
        // 内容实体:完整CRUD
        {
          permission: "data.entity.read",
          effect: "allow",
          policies: [{
            condition: { entity: { $in: ["posts", "pages", "comments", "media"] } },
            effect: "allow",
          }],
        },
        {
          permission: "data.entity.create",
          effect: "allow",
          policies: [{
            condition: { entity: { $in: ["posts", "pages", "comments", "media"] } },
            effect: "allow",
          }],
        },
        {
          permission: "data.entity.update",
          effect: "allow",
          policies: [{
            condition: { entity: { $in: ["posts", "pages", "comments", "media"] } },
            effect: "allow",
          }],
        },
        {
          permission: "data.entity.delete",
          effect: "allow",
          policies: [{
            condition: { entity: { $in: ["posts", "pages", "comments"] } },  // 无媒体删除
            effect: "allow",
          }],
        },
      ],
    },
  },
}

拒绝特定实体

{
  roles: {
    user: {
      implicit_allow: false,
      permissions: [
        // 可以读取大多数实体
        "data.entity.read",
        // 但永远不能访问机密实体
        {
          permission: "data.entity.read",
          effect: "deny",
          policies: [{
            condition: { entity: "secrets" },
            effect: "deny",
          }],
        },
      ],
    },
  },
}

创建辅助函数

用于复杂的角色定义:

// helpers/permissions.ts
type EntityPermission = "read" | "create" | "update" | "delete";

function entityPermissions(
  entities: string[],
  actions: EntityPermission[]
) {
  const permMap: Record<EntityPermission, string> = {
    read: "data.entity.read",
    create: "data.entity.create",
    update: "data.entity.update",
    delete: "data.entity.delete",
  };

  return actions.map((action) => ({
    permission: permMap[action],
    effect: "allow" as const,
    policies: [{
      condition: { entity: { $in: entities } },
      effect: "allow" as const,
    }],
  }));
}

// 用法
{
  roles: {
    blog_author: {
      implicit_allow: false,
      permissions: [
        ...entityPermissions(["posts", "comments"], ["read", "create", "update"]),
        ...entityPermissions(["categories", "tags"], ["read"]),
      ],
    },
  },
}

验证

测试权限分配:

1. 以具有角色的用户登录:

curl -X POST http://localhost:7654/api/auth/password/login \
  -H "Content-Type: application/json" \
  -d '{"email": "editor@example.com", "password": "password123"}'

2. 测试允许的权限:

curl http://localhost:7654/api/data/posts \
  -H "Authorization: Bearer <token>"
# 应该返回200和数据

3. 测试拒绝的权限:

curl -X DELETE http://localhost:7654/api/data/posts/1 \
  -H "Authorization: Bearer <token>"
# 应该返回403禁止访问

4. 测试实体特定权限:

# 如果仅允许帖子/评论:
curl http://localhost:7654/api/data/users \
  -H "Authorization: Bearer <token>"
# 如果用户实体不在允许列表中,应该返回403

常见陷阱

权限未生效

问题: 更改权限后,旧行为仍然存在

修复: 重启服务器 - 角色配置在启动时加载:

# 停止并重启
bknd run

拒绝未覆盖

问题: 拒绝效果未阻止访问

修复: 检查策略条件 - 拒绝仅当条件匹配时应用:

// 错误 - 无条件,可能不匹配
{ permission: "data.entity.delete", effect: "deny" }

// 正确 - 在权限级别简单拒绝
{
  permissions: [
    "data.entity.read",
    "data.entity.create",
    // 根本不包含删除
  ],
}

实体条件不匹配

问题: 实体特定权限不起作用

修复: 验证实体名称是否完全匹配:

// 错误 - 实体名称大小写敏感
{ condition: { entity: "Posts" } }

// 正确 - 使用精确的实体名称
{ condition: { entity: "posts" } }

多个策略冲突

问题: 多个策略导致混淆行为

修复: 理解评估顺序 - 第一个匹配的策略获胜:

{
  policies: [
    // 更具体的先
    { condition: { entity: "secrets" }, effect: "deny" },
    // 通用回退最后
    { effect: "allow" },
  ],
}

变量占位符未解析

问题: @user.id 在过滤器中以字面形式出现

修复: 变量仅适用于 filtercondition 字段:

// 正确用法
{
  condition: { "@id": "@user.id" },  // 有效
  filter: { user_id: "@user.id" },   // 有效
}

注意事项

要做:

  • 从最小权限开始,根据需要添加
  • 对多个实体使用 $in 运算符
  • 添加后测试每个权限
  • 使用描述性策略描述
  • 优先使用明确权限而非 implicit_allow

不要做:

  • 向非管理员角色授予 data.raw.*(SQL注入风险)
  • implicit_allow: true 与拒绝策略一起使用(混淆)
  • 忘记在配置更改后重启服务器
  • 不必要地混合简单字符串和扩展格式
  • 使用太多嵌套策略过度复杂化

相关技能

  • bknd-create-role - 定义新角色
  • bknd-row-level-security - 按用户所有权过滤数据
  • bknd-protect-endpoint - 保护特定端点
  • bknd-public-vs-auth - 配置公共与认证访问
  • bknd-setup-auth - 初始化认证系统