跳到主要内容

什么是 RBAC?Casbin 中的基于角色的访问控制

什么是 RBAC?​

基于角色的访问控制 (RBAC) 将权限授予角色而非单个用户,并为每个用户分配一个或多个角色。要让新员工完成工作,你可以为他们分配诸如 editor 或 billing 之类的角色;要更改所有编辑者的权限,只需修改一次角色即可。

RBAC 包含三个部分:

  • 用户:发起请求的人员或服务。
  • 角色:命名的职能,例如 admin、editor 或 viewer。
  • 权限:对资源执行的操作,例如对 /articles 执行 edit。

当用户的某个角色拥有该权限时,请求即被允许。角色还可以从其他角色继承:如果 editor 继承自 viewer,则编辑者拥有查看者能做的一切操作。RBAC 由 NIST 在 20 世纪 90 年代正式化,RBAC96 模型系列描述了各种变体:扁平角色、角色层级和约束。有关 Casbin 如何映射到这些模型的信息,请参阅 RBAC vs. RBAC96。

Casbin 中最简化的 RBAC 模型和策略:

[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[role_definition]
g = _, _

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

[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
p, editor, article, edit
p, viewer, article, read

g, editor, viewer
g, alice, editor
g, bob, viewer

alice 可以编辑和阅读文章(editor 继承自 viewer);bob 只能阅读它们。

RBAC 与 ABAC: RBAC 易于审计,但如果不为每条记录创建一个角色,就无法表达“仅限自己的记录”。ABAC 添加了对属性(如所有者或时间)的条件限制,大多数应用程序会结合使用两者。选择访问控制模型 在一个示例中比较了 ACL、RBAC、ABAC 和 ReBAC。

教程: 在 Web API 中添加 RBAC,参见 Go with Gin、Node.js with Express、NestJS、Python with FastAPI、Java with Spring Boot、Rust 或 gRPC。你也可以在 在线编辑器 中尝试 RBAC 示例。

角色定义​

[role_definition] 部分定义了 RBAC 角色(继承)关系。Casbin 可以同时运行多个 RBAC 系统:一个用于用户(具有角色继承),另一个可选地用于资源。它们是相互独立的。

此部分是可选的;如果你不使用 RBAC,请省略它。

[role_definition]
g = _, _
g2 = _, _

这里 g 和 g2 是两个角色系统;_, _ 表示每个系统有两端(例如用户和角色)。仅对用户角色使用 g;当资源也有角色时,添加 g2。示例:rbac_model.conf、rbac_with_resource_roles_model.conf。

用户–角色(和资源–角色)映射存储在策略中,例如:

p, data2_admin, data2, read
g, alice, data2_admin

因此 alice 拥有角色 data2_admin。Casbin 将所有名称视为字符串(用户、资源或角色)。

在匹配器中使用角色:

[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act

因此,只有当请求主体拥有策略中指定的角色时,请求才会被允许。

备注
  1. Casbin 仅存储和评估用户–角色(和资源–角色)映射;它不会验证用户或角色是否存在。这是身份验证的工作。
  2. 不要为用户和角色复用相同的名称(例如用户 alice 和角色 alice),因为 Casbin 无法区分它们。如有必要,请使用前缀(例如 role_alice)。
  3. 角色继承是传递性的且无界:如果 A 拥有角色 B,且 B 拥有角色 C,则 A 实际上拥有角色 C。
令牌命名约定

策略中的主体通常命名为 sub 并排在第一位。在 Go Casbin 中,你可以使用其他名称和顺序;之后必须在创建执行器后调用 e.SetFieldIndex("p", constant.SubjectIndex, index),以便 DeleteUser 等 API 使用正确的列。

# `subject` here is for sub
[policy_definition]
p = obj, act, subject
e.SetFieldIndex("p", constant.SubjectIndex, 2) // index starts from 0
ok, err := e.DeleteUser("alice") // without SetFieldIndex, it will raise an error

命名角色系统与权限依赖​

g、g2、g3... 是独立的角色系统。每个系统定义了一个图,匹配器通过相同的名称调用它;g2 不是内置的“权限依赖于权限”关系。像 g2, edit, read 这样的行只是一条边——只有当匹配器询问 g2(...) 时,它才会改变决策。

当你的权限目录存在依赖关系时,这一点很重要,例如“edit 隐含 read”,因此可以编辑的角色也必须允许读取。有两种清晰的建模方式。

当依赖关系是授权的一部分时,使用角色继承。 为每个捆绑包赋予一个角色并让角色继承:

p, editor, article, edit
p, viewer, article, read

g, editor, viewer
g, alice, editor

alice 可以读取,因为 editor 继承自 viewer;不涉及 g2。

当依赖关系本身应存在于策略中时,使用单独的 g2 图。 匹配器定义含义:

[role_definition]
g = _, _
g2 = _, _

[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && (r.act == p.act || g2(p.act, r.act))
p, admin, article, write
g2, write, read
g, alice, admin

请求 alice, article, read 匹配 p, admin, article, write 行,因为 g2(write, read) 为真:write 是 read 的子级,方向与 g 边相同。g2 行本身不授予任何权限,也不受 Casbin 验证;你负责确定每条边的方向和含义,而 Casbin 应用与 g 相同的传递性继承。

如果依赖关系仅是 目录元数据 ——例如,管理员 UI 中角色编辑器应在勾选“edit”时预选“read”——请将其保留在权限目录中,并在生成策略时展开为 p、g 或 g2 行。Casbin 不需要存储从未被评估的数据。策略也不必生活在 CSV 文件中:适配器可以从数据库或你的 YAML/JSON 目录加载生成的行,因此目录仍然是事实来源。有关适配器 API,请参阅 Adapters。

角色层级​

Casbin 实现了 RBAC1 风格的角色层级:如果 alice 拥有 role1,且 role1 拥有 role2,则 alice 实际上拥有 role2 及其权限。

层级深度是指你允许的继承级别数量。默认的角色管理器使用最大深度为 10(可配置),因此用户可以继承最多 10 个级别的角色。

// NewRoleManager is the constructor for creating an instance of the
// default RoleManager implementation.
func NewRoleManager(maxHierarchyLevel int) rbac.RoleManager {
rm := RoleManager{}
rm.allRoles = &sync.Map{}
rm.maxHierarchyLevel = maxHierarchyLevel
rm.hasPattern = false

return &rm
}

区分用户和角色​

在 Casbin 中,用户和角色都是字符串。在扁平 RBAC(无角色层级)中,GetAllSubjects() 和 GetAllRoles() 返回 g 规则的左侧和右侧(通常是用户和角色)。如果有层级结构,同一个名称可能既作为用户又作为角色出现;如果你的应用不跟踪哪个是哪个,请使用命名约定(例如前缀 role::),并在解释结果时进行检查。

隐式角色和权限​

通过层级继承的角色和权限(不仅仅是直接分配)被称为 隐式。使用 GetImplicitRolesForUser() 和 GetImplicitPermissionsForUser() 来包含它们;GetRolesForUser() 和 GetPermissionsForUser() 仅返回直接分配。参见 GitHub #137。

RBAC 中的模式匹配​

参见 带模式的 RBAC。

角色管理器​

有关自定义角色管理,请参见 角色管理器。

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