Ory Keto不是策略决策树,而是基于Zanzibar的关系型权限系统,仅通过check接口回答“主体对对象是否具有某关系”的布尔问题;所有逻辑必须预先建模为关系元组,不支持运行时条件判断或跨namespace自动回退。

Ory Keto 不是“策略访问决策树”,它是基于 Google Zanzibar 模型的关系型权限系统,核心是关系元组((namespace, object, relation, subject))和细粒度的 check 查询。把它当“决策树”用,容易误配策略逻辑、查不出权限、或性能崩掉。
为什么不能把 Keto 当成决策树直接调用?
因为 Keto 的 check 接口不执行策略编译或条件分支判断——它只回答“给定主体对某对象是否具有某关系”这一布尔问题。所谓“决策路径”实际由你写的 relation tuple 结构 + 命名空间定义 + 可选的 subject_set 间接关系共同构成,不是靠 if-else 或 Rego 那样的规则引擎驱动。
-
check是单点查询,不是递归策略求值;Keto 不解析表达式,也不支持if user.role == "admin"这类运行时条件 - 所有“逻辑”必须提前建模为元组,比如
(videos, video:123, viewer, user:alice)或(videos, video:123, viewer, group:staff),再通过group成员关系间接授权 - 命名空间(
namespace)必须严格隔离,跨 namespace 的check默认失败,不会自动 fallback 或 merge
Go 客户端怎么安全发起 check 请求?
别手写 HTTP client 调 /engines/acp/ory/check,用官方 SDK ory/keto-client-go,它处理了重试、超时、错误码映射和 context 传递。
- 初始化 client 时必须设
context.WithTimeout,Ketocheck默认无超时,网络抖动会卡住整个请求链 - 错误要区分
ErrorResponseException(如 403/404)和底层连接错误(net.OpError),前者是策略拒绝,后者需重试或降级 - 不要在
check参数里拼接用户输入的object或subject,先做白名单校验,否则可能触发 Keto 的 namespace 匹配失败或 SQL 注入(DSN 为 PostgreSQL 时)
示例关键片段:
client := keto.NewClient("http://keto:4466")
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
defer cancel()
res, err := client.CheckPermission(ctx, &keto.CheckPermissionRequest{
Namespace: "videos",
Object: "video:123",
Relation: "view",
Subject: "user:alice",
})
if err != nil {
if _, ok := err.(*keto.ErrorResponseException); ok {
// 明确拒绝:返回 403
return http.StatusForbidden
}
// 其他错误:网络、超时等
return http.StatusInternalServerError
}
if !res.Allowed {
return http.StatusForbidden
}
如何避免命名空间和元组爆炸导致维护失控?
一个常见坑是每个微服务自己管理自己的命名空间和元组写入,结果 videos、video、media 多个 namespace 并存,view、can_view、viewer 关系混用,user:alice 和 alice@domain.com 主体格式不统一。
立即学习“go语言免费学习笔记(深入)”;
- 所有命名空间定义必须集中管控,放在 GitOps 配置仓库中,Keto 启动时通过
--config加载,禁止运行时动态增删 - 元组写入必须走统一 service(比如
keto-writer),该 service 校验object格式(如^video:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$)、subject类型(只允许user:、group:、role:前缀) - 禁止前端或业务代码直接调
/engines/acp/ory/relation-tuples写入,所有变更走事件驱动(如 Kafka event → writer service)
与 Ory Oathkeeper 集成时最容易漏掉的配置点
很多人以为配完 Oathkeeper 的 authz 策略就万事大吉,但实际常因三处细节失效:
- Oathkeeper 的
authz.keto配置里,allowed_scopes必须显式列出当前 rule 所需的 relation,比如["view", "edit"],否则 Keto 返回 400invalid relation - Keto 的
serve.read.host地址必须能被 Oathkeeper 容器网络访问(不是localhost),且 Oathkeeper 的authz.keto.url要指向这个地址,不是http://keto:4466就一定通 - 若使用 JWT bearer token,Oathkeeper 的
authenticator.jwt必须正确提取sub或自定义 claim 作为subject,否则 Keto 查不到对应元组——常见错误是 JWT 里是user_id字段,但没在authenticator.jwt.subject中映射
(approvals, expense:789, approver, user:bob) + (departments, dept:finance, member, user:bob) + (approvals, expense:789, department, dept:finance) 三层元组,而不是指望 Keto 自己推导。这一步建模错了,后面所有 check 都不可信。


















