第十四章:OpenFGA — 细粒度授权引擎
“OpenFGA 让你像 Google 一样管理权限 — 基于关系,而非角色。”
mindmap
root((OpenFGA))
核心概念
Type
Relation
Tuple
API
Check
ListObjects
Expand
Write
授权模型
DSL
Google Drive
GitHub
多租户
部署
Server
PostgreSQL
SDK
14.1 OpenFGA 是什么
OpenFGA(Fine-Grained Authorization)是由 Auth0/Okta 开源的细粒度授权引擎,基于 Google Zanzibar 论文实现。它是 CNCF Sandbox 项目。
OpenFGA 架构:
┌──────────┐ ┌──────────────────┐ ┌──────────┐
│ 应用 │───▶│ OpenFGA Server │───▶│ 数据存储 │
│ │ │ │ │PostgreSQL│
│ Check() │ │ ┌────────────┐ │ │ MySQL │
│ Write() │ │ │ 授权模型 │ │ │ │
│ List() │ │ │ 关系元组 │ │ │ │
│ │ │ │ 图遍历引擎 │ │ │ │
└──────────┘ │ └────────────┘ │ └──────────┘
└──────────────────┘
14.2 核心概念
关系元组(Relationship Tuple)
# 格式:object#relation@subject
# Alice 是 budget 文档的 owner
document:budget#owner@user:alice
# Bob 是 budget 文档的 editor
document:budget#editor@user:bob
# Acme 组织的所有 member 都是 roadmap 文档的 viewer
document:roadmap#viewer@organization:acme#member
# Carol 是 Acme 组织的 member
organization:acme#member@user:carol
→ 因此 Carol 可以查看 roadmap 文档(通过关系链推导)
API 操作
API |
功能 |
示例 |
|---|---|---|
Check |
检查是否有权限 |
Carol 能查看 roadmap 吗?→ ✅ |
ListObjects |
列出有权限的对象 |
Carol 能查看哪些文档? |
ListUsers |
列出有权限的用户 |
谁能编辑 budget? |
Expand |
展开权限关系树 |
budget 的 viewer 包含哪些人? |
Write |
写入关系元组 |
添加/删除权限关系 |
Read |
读取关系元组 |
查询已有的权限关系 |
14.3 实战场景:Google Drive 权限模型
model
schema 1.1
type user
type group
relations
define member: [user, group#member]
type folder
relations
define owner: [user]
define editor: [user, group#member] or owner
define viewer: [user, group#member] or editor
define parent: [folder]
# 继承父文件夹的权限
define can_edit: editor or owner or can_edit from parent
define can_view: viewer or can_edit or can_view from parent
type document
relations
define owner: [user]
define editor: [user, group#member] or owner
define viewer: [user, group#member] or editor
define parent: [folder]
define can_edit: editor or owner or can_edit from parent
define can_view: viewer or can_edit or can_view from parent
define can_share: owner
define can_delete: owner
关系元组示例:
# 文件夹层次
folder:engineering#parent@folder:root
folder:backend#parent@folder:engineering
# 文档归属
document:api-spec#parent@folder:backend
# 权限分配
folder:root#viewer@group:all-employees#member
folder:engineering#editor@group:eng-team#member
document:api-spec#owner@user:alice
# 权限推导:
# Bob 是 eng-team 的 member
# → Bob 是 engineering 文件夹的 editor
# → Bob 是 backend 文件夹的 editor(继承)
# → Bob 可以编辑 api-spec 文档(继承)
14.4 Python SDK 完整示例
import asyncio
from openfga_sdk import (
ClientConfiguration,
OpenFgaClient,
ClientWriteRequest,
ClientTupleKey,
ClientCheckRequest,
ClientListObjectsRequest,
)
from openfga_sdk.client.models import WriteAuthorizationModelRequest
async def main():
# 1. 配置客户端
config = ClientConfiguration(
api_url="http://localhost:8080",
store_id="your-store-id",
)
async with OpenFgaClient(config) as client:
# 2. 创建授权模型
model = WriteAuthorizationModelRequest(
schema_version="1.1",
type_definitions=[
{"type": "user"},
{
"type": "document",
"relations": {
"owner": {"this": {}},
"editor": {"this": {}},
"viewer": {
"union": {
"child": [
{"this": {}},
{"computedUserset": {"relation": "editor"}},
{"computedUserset": {"relation": "owner"}},
]
}
},
},
"metadata": {
"relations": {
"owner": {"directly_related_user_types": [{"type": "user"}]},
"editor": {"directly_related_user_types": [{"type": "user"}]},
"viewer": {"directly_related_user_types": [{"type": "user"}]},
}
},
},
],
)
auth_model = await client.write_authorization_model(model)
print(f"模型 ID: {auth_model.authorization_model_id}")
# 3. 写入关系元组
await client.write(ClientWriteRequest(
writes=[
ClientTupleKey(
user="user:alice",
relation="owner",
object="document:budget",
),
ClientTupleKey(
user="user:bob",
relation="editor",
object="document:budget",
),
ClientTupleKey(
user="user:carol",
relation="viewer",
object="document:budget",
),
]
))
print("关系元组写入成功")
# 4. 检查权限
result = await client.check(ClientCheckRequest(
user="user:bob",
relation="viewer",
object="document:budget",
))
print(f"Bob 能查看 budget? {result.allowed}") # True(editor 隐含 viewer)
result = await client.check(ClientCheckRequest(
user="user:carol",
relation="editor",
object="document:budget",
))
print(f"Carol 能编辑 budget? {result.allowed}") # False
# 5. 列出用户可访问的文档
objects = await client.list_objects(ClientListObjectsRequest(
user="user:alice",
relation="viewer",
type="document",
))
print(f"Alice 可查看的文档: {objects.objects}")
asyncio.run(main())
14.5 OpenFGA 部署
# docker-compose.yml
services:
postgres:
image: postgres:16
environment:
POSTGRES_USER: openfga
POSTGRES_PASSWORD: openfga_password
POSTGRES_DB: openfga
volumes:
- pgdata:/var/lib/postgresql/data
openfga-migrate:
image: openfga/openfga:latest
command: migrate
environment:
OPENFGA_DATASTORE_ENGINE: postgres
OPENFGA_DATASTORE_URI: postgres://openfga:openfga_password@postgres:5432/openfga?sslmode=disable
depends_on:
- postgres
openfga:
image: openfga/openfga:latest
command: run
environment:
OPENFGA_DATASTORE_ENGINE: postgres
OPENFGA_DATASTORE_URI: postgres://openfga:openfga_password@postgres:5432/openfga?sslmode=disable
ports:
- "8080:8080" # HTTP API
- "8081:8081" # gRPC API
- "3000:3000" # Playground
depends_on:
openfga-migrate:
condition: service_completed_successfully
volumes:
pgdata:
14.6 OpenFGA vs RBAC
维度 |
RBAC |
OpenFGA (ReBAC) |
|---|---|---|
授权依据 |
角色 |
关系 |
粒度 |
角色级 |
对象级 |
“谁能访问X” |
需要遍历所有角色 |
直接查询 |
“X能访问什么” |
需要遍历所有资源 |
ListObjects API |
继承 |
角色层次 |
关系链(更灵活) |
性能 |
O(1) 角色检查 |
图遍历(有缓存) |
适用场景 |
简单权限 |
协作应用、SaaS |
14.7 小结
OpenFGA 基于 Google Zanzibar,提供关系驱动的细粒度授权
核心是关系元组:
object#relation@subject支持权限继承(通过关系链推导)
提供 Check、ListObjects、ListUsers 等丰富的 API
适合协作应用(文档、项目管理)和多租户 SaaS
与 RBAC 相比,OpenFGA 在资源级别的细粒度控制上更有优势