使用jCasbin进行授权
1. 简介
本教程中,我们将了解 jCasbin——流行 Apache Casbin 授权库的 Java 官方移植版。我们将介绍它的概念、使用方法以及能实现的功能。
2. 什么是 jCasbin
jCasbin 是功能强大的 Casbin 访问控制库的 Java 版本,它能帮助我们轻松回答应用中有关资源访问的问题。
jCasbin 以及整个 Casbin 库套件,通过一种配置模型来运行。该模型允许我们描述想要使用的访问控制模型(例如 ACL 或 RBAC),并配合单独的策略数据应用于该配置模型。
通常,这需要定义:
- 我们希望控制访问的对象。
- 想要访问这些对象的主体。
- 主体希望对对象执行的操作。
不过,jCasbin 足够灵活,只要能够正确地在配置和策略数据中建模,我们可以使用任何所需的结构。
3. 依赖项
使用 jCasbin 之前,我们需要在构建中包含最新版本,截至撰写本文时为 1.99.0。
如果使用 Maven,可以在 pom.xml 文件中加入以下依赖:
至此,即可开始在应用中使用它。
4. 创建 Enforcer
在应用中引入 jCasbin 后,即可开始使用。核心类是 Enforcer,构造它需要两个数据源:我们的配置模型和策略数据。 最简单的方式是传入文件名:
然而,将策略数据放在本地文件可能不太方便。因此,jCasbin 允许在此处使用 Adapter,它提供从任何数据源加载和操作策略数据的手段。我们还有一组标准适配器可供应用使用,包括熟悉的技术如 JDBC、Hibernate、MongoDB 等:
这些适配器具体如何工作取决于具体适配器,不在本文讨论范围。
当以文件名指定策略数据时,内部会使用 FileAdapter。我们也可以通过提供 InputStream 从其他来源加载文件:
同样,我们可以使用 Model 实例来提供模型数据,从而从其他来源加载模型数据:
创建好 Enforcer 后,即可开始用它检查权限。
5. 权限强制执行
现在我们已经有了 Enforcer,可以开始检查权限了。通过 enforce() 方法实现,通常需要传入主体、对象和操作:
注意 enforce() 方法实际接受任意一组对象,具体取决于配置模型的定义。
权限检查的具体方式取决于配置模型的结构和策略数据。
5.1. 访问控制列表 (ACL)
配置 jCasbin 最简单的方式之一是通过 ACL。 典型的配置模型如下:
其中,request_definition 部分定义了传入 enforcer.enforce() 的数据格式,为传入的参数提供了名称(这里用 "sub"、"obj"、"act" 分别表示 "主体"、"对象"、"操作")。
policy_definition 部分使用相同的名称映射到策略数据。如果没有指定,还有一个隐式字段 "eft"(效果),默认值为 "allow"。
matchers 部分说明如何将策略行与请求匹配。这里,我们简单地要求每个字段的值必须完全相同。
最后,policy_effect 部分说明了如何确定策略的整体效果。只有当此部分返回正匹配时,操作才被允许。
在这种模式下,策略数据就是主体、对象和允许操作的直接列表——由 policy_definition 定义:
这表示主体 "alice" 可以对对象 "data1" 执行 "read" 操作,主体 "bob" 可以对对象 "data2" 执行 "write" 操作。
其他任何检查都会被拒绝,因为它们不匹配我们的配置。
5.2. 超级用户
有时可能需要超级用户——无论策略数据如何,都可以执行任何操作的用户。
我们可以通过调整 matchers 部分的定义来在配置模型中表达这一点:
这里,除了之前的匹配逻辑外,当主体值精确为 "root" 时也会匹配,无论策略表达式的其余部分如何。这样,主体为 "root" 的用户始终能通过所有检查:
这会给权限检查引入一定风险,因此需要谨慎使用。
5.3. 基于角色的访问控制 (RBAC)
RBAC 为配置模型引入了额外的间接层。 在此情况下,我们为用户分配角色,并基于角色定义权限。
首先添加 role_definition 部分:
这定义了可用于定义角色成员关系的 g() 函数。
同时需要更新 matchers 部分以利用这一点:
这里不再是直接比较策略主体与请求,而是要求它们必须通过角色定义匹配。
现在可以在策略数据中使用它:
所有以 p 开头的行定义了权限分配方式(直接分配给用户或角色)。这里我们将一个权限直接分配给主体 "alice",将两个权限分配给角色 "data2_admin"。
以 g 开头的行(来自上面的 role_definition 部分)定义了角色成员关系。这里我们定义主体 "bob" 被分配了角色 "data2_admin"。这意味着该主体隐式拥有此角色的所有权限:
我们还可以嵌套角色,即将一个角色分配给另一个角色,从而生成完整的权限层次结构:
这里,角色 "data2_admin" 被分配给角色 "superuser",而角色 "superuser" 又被分配给主体 "carol"。这意味着该主体拥有最终的所有权限:
我们从未直接或间接授予 "carol" 对 "data2" 的权限。她通过自己的角色 "superuser" 继承而来。
6. 管理 API
除了检查权限,我们还有用于管理配置的管理 API。
这些操作通过我们已经使用的同一个 Enforcer 实例完成。
6.1. 查询主体、对象和操作
最简单的操作是查询策略数据中的所有主体和对象。 使用 getAllSubjects()、getAllObjects() 和 getAllActions() 方法:
我们还可以查询任意主体对任意对象可以执行的操作:
这对 ACL 和 RBAC 设置都适用,也适用于层级角色。
6.2. 查询 RBAC 角色
在 RBAC 设置下,我们还可以查询哪些角色被分配给了哪些主体:
这仅返回直接分配的角色,不包括层级中包含的角色。
不过,我们也可以类似地查询角色之间的分配:
这意味着如果需要,我们可以遍历层级结构。
6.3. 管理权限和角色
我们还有一组方法可以用来直接管理主体拥有的权限和角色。
我们可以直接添加和删除主体与对象之间的权限:
这些操作一旦调用立即生效,权限检查会马上受到影响。
在 RBAC 模式下工作时,我们还可以从主体添加和删除角色:
所有这些更改都在内存中进行。我们需要使用 savePolicy() 方法将它们写回后端存储:
但这只有在使用特定适配器时才能实现,也取决于数据的加载方式。例如,如果从 InputStream 加载,则无法写回。
7. 总结
本文快速介绍了 jCasbin。它还有更多功能可以探索。下次需要为应用管理访问控制时,不妨尝试一下。
和往常一样,本文的所有代码可在 GitHub 上找到。