Appearance
SpiceDB 是一个开源的、专门用于存储、计算和验证应用程序权限的数据库。
它的灵感来源于 Google 的全球授权系统 Zanzibar。你可以把它想象成一个为你的所有应用和服务提供“权限决策”的中央大脑。
传统的权限管理通常是在代码中写的,比如:
java
if (user.getRole().equals("admin") || user.getId().equals(document.getOwnerId())) {
// allow access
}
这种方式有很多弊端:
- 逻辑分散:权限代码散落在各个服务的各个角落,难以维护和审计。
- 不够灵活:如果想增加一个新的角色(比如“协作者”),或者增加一种新的关系(比如“团队成员可以查看”),就需要修改大量代码并重新部署。
- 不一致:不同服务对权限的实现可能不一致,导致安全漏洞。
SpiceDB 解决的就是这个问题。它将权限逻辑从你的应用代码中抽离出来,变成可管理、可查询的“数据”。
SpiceDB 的核心思想是 "关系式授权" (Relationship-Based Access Control, ReBAC)。它通过以下三个基本元素来描述权限:
- 对象 (Object):需要被保护的资源,例如
document:123
(一个文档)、folder:abc
(一个文件夹)。 - 主体 (Subject):发起访问请求的实体,例如
user:alice
(一个用户)、team:developers
(一个团队)。 - 关系 (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
的方式有三种:- 被直接授予
viewer
权限。 - 是某个被授权团队的
member
(team#member
)。 - 拥有
editor
权限(因为编辑者自然可以查看)。
- 被直接授予
2. 存储关系数据 (Writing Relationships)
现在,我们有了一个权限蓝图 (Schema),接下来就可以向 SpiceDB 中写入具体的关系了。假设我们有以下场景:
- 用户
alice
创建了一个文档secrets_project
。 - 用户
bob
是engineering
团队的成员。 alice
将engineering
团队设置为文档secrets_project
的查看者。alice
直接授予了用户carl
对文档secrets_project
的编辑权限。
这些场景会转化为以下关系元组存入 SpiceDB:
document:secrets_project#owner@user:alice
- (Alice 是 secrets_project 的拥有者)
team:engineering#member@user:bob
- (Bob 是 engineering 团队的成员)
document:secrets_project#viewer@team:engineering
- (engineering 团队是 secrets_project 的查看者)
document:secrets_project#editor@user:carl
- (Carl 是 secrets_project 的编辑者)
3. 检查权限 (Checking Permissions)
当用户在我们的应用中尝试访问资源时,我们的后端服务不再需要自己编写复杂的 if-else
逻辑,而是直接向 SpiceDB 发起一个简单的查询。
场景一:Alice (拥有者) 尝试编辑文档
- 应用提问:
check(user:alice, "edit", document:secrets_project)
? - SpiceDB 的思考过程:
- Alice 是
editor
吗?Schema 说editor
可以是owner
。 - 那么 Alice 是
owner
吗? - 查询关系数据,发现
document:secrets_project#owner@user:alice
存在。 - 所以,Alice 是
owner
,因此她也是editor
。
- Alice 是
- SpiceDB 回答:允许 (Allow)
场景二:Bob (团队成员) 尝试查看文档
- 应用提问:
check(user:bob, "view", document:secrets_project)
? - SpiceDB 的思考过程:
- Bob 是
viewer
吗?Schema 说viewer
可以是team#member
。 - 哪个团队被授予了查看权限?查询关系数据,发现是
team:engineering
。 - 那么 Bob 是
team:engineering
的member
吗? - 查询关系数据,发现
team:engineering#member@user:bob
存在。 - 所以,Bob 通过团队成员身份获得了查看权限。
- Bob 是
- SpiceDB 回答:允许 (Allow)
场景三:Bob (团队成员) 尝试编辑文档
- 应用提问:
check(user:bob, "edit", document:secrets_project)
? - SpiceDB 的思考过程:
- Bob 是
editor
吗?Schema 说editor
可以是user
或owner
。 document:secrets_project#editor@user:bob
存在吗?不存在。document:secrets_project#owner@user:bob
存在吗?不存在。- 没有其他路径能让 Bob 成为
editor
。
- Bob 是
- SpiceDB 回答:拒绝 (Deny)
场景四:Carl (编辑者) 尝试查看文档
- 应用提问:
check(user:carl, "view", document:secrets_project)
? - SpiceDB 的思考过程:
- Carl 是
viewer
吗?Schema 说viewer
可以是editor
。 - 那么 Carl 是
editor
吗? - 查询关系数据,发现
document:secrets_project#editor@user:carl
存在。 - 所以,Carl 是
editor
,因此他也是viewer
。
- Carl 是
- SpiceDB 回答:允许 (Allow)