跳到主要内容

什么是 ABAC?基于属性的访问控制及示例

什么是 ABAC?​

基于属性的访问控制 (ABAC) 通过评估属性与规则的匹配情况来授予或拒绝请求,而不是查询用户是谁或他们拥有哪个角色。NIST 在 SP 800-162 中发布了参考定义,将输入分为四类:

属性所属对象示例
主体(用户或服务)部门、权限级别、雇佣类型、年龄
客体(资源)所有者、部门、密级、状态
操作读取、编辑、批准、删除
环境时间、IP 地址或网络、设备、位置

规则是针对这些属性的条件,例如“如果用户与文档属于同一部门,且在工作时间内,则可以读取该文档”。由于规则引用的是属性而非命名的用户或角色,因此无需添加任何新规则即可覆盖新用户和新文档。

Casbin 使 ABAC 保持简单:将结构体或对象传入 Enforce(),并在匹配器中引用其字段。完整的应用示例请参见 Go、Node.js with Express 和 Python with Flask 的 ABAC 教程。

ABAC 与 RBAC 对比​

RBAC 基于用户的角色进行决策:“编辑者可以编辑文档”。ABAC 基于属性进行决策:“所有者可以编辑该文档”。角色易于审计,因为你可以列出某个角色允许的操作以及拥有该角色的用户;而属性则表达了关于特定记录和上下文的规则,如所有权、部门或时间,这些是仅靠角色无法表达的,除非为每条记录创建一个角色。大多数应用会结合使用两者:角色决定用户可以访问哪些功能,而属性条件则进一步缩小到他们可以触达的记录范围。选择访问控制模型 通过一个示例比较了 ACL、RBAC、ABAC 和 ReBAC,并展示了如何在单个 Casbin 模型中将 RBAC 与 ABAC 结合使用。

优势与劣势​

  • 擅长: 所有权(“仅限自己的”)、上下文(工作时间、办公网络),以及带有自身标签的数据(部门、区域、敏感度)。一条规则可以替代许多角色授权。
  • 不擅长: 审计。“谁可以读取此文档?”没有直接答案;你需要针对每个用户评估规则。此外,规则还需要手头的属性,因此应用程序通常会在检查之前加载记录。

最小示例:

[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = r.sub == r.obj.Owner

因此 r.obj 必须是一个包含 Owner 字段的对象;Casbin 通过反射读取它。在 Go 中,你可能会定义如下内容:

type testResource struct {
Name string
Owner string
}

你也可以传入 JSON 字符串;启用 JSON 处理后,Casbin 将解析它们:

e, _ := NewEnforcer("examples/abac_model.conf")
e.EnableAcceptJsonRequest(true)

data1Json := `{ "Name": "data1", "Owner": "bob"}`

ok, err := e.Enforce("alice", data1Json, "read")
if err != nil {
// Handle JSON parsing errors
fmt.Printf("Enforcement failed: %v\n", err)
return
}

启用后,任何以 { 或 [ 开头的参数都将被解析为 JSON。无效的 JSON 会产生清晰的错误信息;其他字符串则保持不变。

// Valid JSON - automatically parsed
validJSON := `{"Name": "alice", "Age": 25}`
ok, err := e.Enforce(validJSON, "data1", "read") // Works correctly

// Invalid JSON - returns descriptive error
invalidJSON := `{"Name": "alice",}` // Trailing comma is invalid in JSON
ok, err := e.Enforce(invalidJSON, "data1", "read")
// Returns: "failed to parse JSON parameter at index 0: ..."

// Plain string - no parsing attempted
plainString := "alice"
ok, err := e.Enforce(plainString, "data1", "read") // Treated as plain string
备注

启用 JSON 参数支持会带来大约 1.1 到 1.5 倍的性能开销。

环境属性​

请求可以携带除 sub、obj 和 act 之外的更多信息。添加一个用于环境的字段,并像传递其他对象一样将其传递给 Enforce()。以下模型允许所有者随时编辑其文档,并允许同部门的人员在 9:00 至 18:00 之间读取该文档:

[request_definition]
r = sub, obj, act, env

[policy_definition]
p = sub, obj, act

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = r.sub.Name == r.obj.Owner || (r.act == "read" && r.sub.Department == r.obj.Department && r.env.Hour >= 9 && r.env.Hour < 18)
type User struct{ Name, Department string }
type Doc struct{ Name, Owner, Department string }
type Env struct{ Hour int }

doc := Doc{Name: "design.md", Owner: "alice", Department: "design"}
bob := User{Name: "bob", Department: "design"}

ok, _ := e.Enforce(bob, doc, "read", Env{Hour: 10}) // true
ok, _ = e.Enforce(bob, doc, "read", Env{Hour: 22}) // false: outside business hours
ok, _ = e.Enforce(bob, doc, "edit", Env{Hour: 10}) // false: bob is not the owner

使用 ABAC​

  1. 在匹配器中引用属性(例如 r.sub.Role、r.obj.Owner)。
  2. 将结构体或类实例(或 JSON)传入 Enforce()。
注意

ABAC 属性访问仅适用于请求元素(r.sub、r.obj、r.act)。策略元素(如 p.sub)不能使用 ABAC,因为策略无法存储结构体或类定义。

提示

匹配器可以同时引用多个属性。示例:m = r.sub.Domain == r.obj.Domain。

提示

当策略中包含与 CSV 解析冲突的逗号时,请用引号包裹表达式。示例:"keyMatch("bob", r.sub.Role)" 可防止意外拆分。

使用 eval() 处理复杂规则​

对于更复杂的条件,请将表达式放入 policy 并使用 eval() 进行评估,以保持模型精简且策略灵活。

使用 eval() 的模型配置:

[request_definition]
r = sub, obj, act

[policy_definition]
p = sub_rule, obj, act

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = eval(p.sub_rule) && r.obj == p.obj && r.act == p.act

p.sub_rule 是关于请求属性的表达式,在执行 enforce 时进行评估。示例策略:

p, r.sub.Age > 18, /data1, read
p, r.sub.Age < 60, /data2, write

常见问题 (FAQ)​

ABAC 比 RBAC 更好吗? 总体而言,两者并无优劣之分。ABAC 能表达更细粒度的规则;RBAC 更易于审查。建议从角色开始,并在角色不再适用时(如所有权或时间窗口)添加属性条件。

ABAC 和 PBAC 有什么区别? ABAC 说明了决策依据是什么(属性)。PBAC 说明了规则如何管理:作为在运行时评估的策略数据。上述 eval() 模式兼具两者特点:将属性条件存储为策略。

ABAC 等同于 XACML 吗? 不等同。XACML 是一种用于编写 ABAC 策略的 OASIS 标准语言。Casbin 不使用 XACML;规则写在模型的匹配器中或存储为策略行。

我可以不写代码就尝试 ABAC 吗? 可以。在在线编辑器中选择一个 ABAC 示例,然后编辑匹配器和请求。

Comments are powered by giscus and GitHub Discussions. They load from giscus.app and GitHub only after you click the button below.