Skip to content

SpiceDB 是一个开源的、专门用于存储、计算和验证应用程序权限的数据库

它的灵感来源于 Google 的全球授权系统 Zanzibar。你可以把它想象成一个为你的所有应用和服务提供“权限决策”的中央大脑。

传统的权限管理通常是在代码中写的,比如:

java
if (user.getRole().equals("admin") || user.getId().equals(document.getOwnerId())) {
    // allow access
}

这种方式有很多弊端:

  • 逻辑分散:权限代码散落在各个服务的各个角落,难以维护和审计。
  • 不够灵活:如果想增加一个新的角色(比如“协作者”),或者增加一种新的关系(比如“团队成员可以查看”),就需要修改大量代码并重新部署。
  • 不一致:不同服务对权限的实现可能不一致,导致安全漏洞。

SpiceDB 解决的就是这个问题。它将权限逻辑从你的应用代码中抽离出来,变成可管理、可查询的“数据”。

SpiceDB 的核心思想是 "关系式授权" (Relationship-Based Access Control, ReBAC)。它通过以下三个基本元素来描述权限:

  1. 对象 (Object):需要被保护的资源,例如 document:123 (一个文档)、folder:abc (一个文件夹)。
  2. 主体 (Subject):发起访问请求的实体,例如 user:alice (一个用户)、team:developers (一个团队)。
  3. 关系 (Relation):主体和对象之间的联系,例如 owner (拥有者)、editor (编辑者)、viewer (查看者)。

这三者构成一个“关系元组” (Relationship Tuple),格式为:对象#关系@主体

例如:document:123#owner@user:alice 就表示“用户 alice 是文档 123 的拥有者”。

一个实际的例子:类似 Google Docs 的协作文档系统

假设我们正在构建一个简单的协作文档平台,用户可以创建文档,并分享给其他人或整个团队。

1. 定义权限模型 (Schema)

首先,我们需要在 SpiceDB 中定义我们的权限模型。这就像是为数据库设计表结构一样,我们定义有哪些类型的对象和它们之间可能存在的关系。

sdl
// 定义“用户”类型,它很简单,没有特殊关系
definition user {}

// 定义“团队”类型
definition team {
    // 团队有“成员”,成员是用户
    relation member: user
}

// 定义“文档”类型
definition document {
    // 文档有“拥有者”,拥有者是用户
    relation owner: user
    
    // “编辑者”可以是直接指定的用户,也可以是文档的拥有者
    relation editor: user | owner
    
    // “查看者”可以是直接指定的用户、团队的成员、文档的编辑者
    // 这展示了 SpiceDB 强大的组合能力!
    relation viewer: user | team#member | editor
}

对这个 Schema 的解读:

  • definition user {}definition team {} 定义了两种主体类型。
  • team 有一个 member 关系,表示团队由哪些 user 组成。
  • document 的权限模型最有趣:
    • relation editor: user | owner:这行代码表示,一个用户只要是 owner,他就自动拥有 editor 的权限。我们不需要再手动为他添加 editor 关系。
    • relation viewer: user | team#member | editor:这行代码表示,成为 viewer 的方式有三种:
      1. 被直接授予 viewer 权限。
      2. 是某个被授权团队的 member (team#member)。
      3. 拥有 editor 权限(因为编辑者自然可以查看)。

2. 存储关系数据 (Writing Relationships)

现在,我们有了一个权限蓝图 (Schema),接下来就可以向 SpiceDB 中写入具体的关系了。假设我们有以下场景:

  • 用户 alice 创建了一个文档 secrets_project
  • 用户 bobengineering 团队的成员。
  • aliceengineering 团队设置为文档 secrets_project 的查看者。
  • alice 直接授予了用户 carl 对文档 secrets_project 的编辑权限。

这些场景会转化为以下关系元组存入 SpiceDB:

  1. document:secrets_project#owner@user:alice
    • (Alice 是 secrets_project 的拥有者)
  2. team:engineering#member@user:bob
    • (Bob 是 engineering 团队的成员)
  3. document:secrets_project#viewer@team:engineering
    • (engineering 团队是 secrets_project 的查看者)
  4. document:secrets_project#editor@user:carl
    • (Carl 是 secrets_project 的编辑者)

3. 检查权限 (Checking Permissions)

当用户在我们的应用中尝试访问资源时,我们的后端服务不再需要自己编写复杂的 if-else 逻辑,而是直接向 SpiceDB 发起一个简单的查询。

场景一:Alice (拥有者) 尝试编辑文档

  • 应用提问check(user:alice, "edit", document:secrets_project)?
  • SpiceDB 的思考过程
    1. Alice 是 editor 吗?Schema 说 editor 可以是 owner
    2. 那么 Alice 是 owner 吗?
    3. 查询关系数据,发现 document:secrets_project#owner@user:alice 存在。
    4. 所以,Alice 是 owner,因此她也是 editor
  • SpiceDB 回答允许 (Allow)

场景二:Bob (团队成员) 尝试查看文档

  • 应用提问check(user:bob, "view", document:secrets_project)?
  • SpiceDB 的思考过程
    1. Bob 是 viewer 吗?Schema 说 viewer 可以是 team#member
    2. 哪个团队被授予了查看权限?查询关系数据,发现是 team:engineering
    3. 那么 Bob 是 team:engineeringmember 吗?
    4. 查询关系数据,发现 team:engineering#member@user:bob 存在。
    5. 所以,Bob 通过团队成员身份获得了查看权限。
  • SpiceDB 回答允许 (Allow)

场景三:Bob (团队成员) 尝试编辑文档

  • 应用提问check(user:bob, "edit", document:secrets_project)?
  • SpiceDB 的思考过程
    1. Bob 是 editor 吗?Schema 说 editor 可以是 userowner
    2. document:secrets_project#editor@user:bob 存在吗?不存在。
    3. document:secrets_project#owner@user:bob 存在吗?不存在。
    4. 没有其他路径能让 Bob 成为 editor
  • SpiceDB 回答拒绝 (Deny)

场景四:Carl (编辑者) 尝试查看文档

  • 应用提问check(user:carl, "view", document:secrets_project)?
  • SpiceDB 的思考过程
    1. Carl 是 viewer 吗?Schema 说 viewer 可以是 editor
    2. 那么 Carl 是 editor 吗?
    3. 查询关系数据,发现 document:secrets_project#editor@user:carl 存在。
    4. 所以,Carl 是 editor,因此他也是 viewer
  • SpiceDB 回答允许 (Allow)