Keycloak中的资源、作用域、权限和策略

196

我想使用Keycloak的授权系统创建一个相当简单的基于角色的访问控制系统。Keycloak正在替换的系统允许我们创建一个“用户”,该用户是一个或多个“组”的成员。在这个传统系统中,用户通过组成员身份(其中分配了权限)或直接授予权限给用户来获得访问大约250个“功能”的每一个“权限”。

我想将传统系统映射到Keycloak授权中。

对于我来说,将现有系统中的每个“功能”映射到Keycloak资源和一组Keycloak范围应该很简单。例如,“viewAccount”功能显然会映射到“帐户”资源和“查看”范围;而“viewTransaction”则映射到“交易”资源...但是,最佳实践是创建一个“查看”范围,并在多个资源(帐户、交易等)之间使用它吗?还是应该创建一个“viewAccount”范围、“viewTransaction”范围等?

同样地,我对权限也有点困惑。对于每个资源和范围的实际组合,通常创建一个权限是常规做法吗?如果有多个匹配给定资源/范围的权限,Keycloak会怎么做?我猜Keycloak的目的是允许我配置针对资源和范围的权限矩阵,例如我可以获得访问"账户"的权限和"查看"范围的权限,因此我就有查看账户的权限了?
我问这个问题是因为所有这些的结果似乎是我的旧的"viewAccount"能力最终创建了一个"Account"资源,具有"View"范围和一个"viewAccount"权限,这似乎让我回到了起点。如果这是正确的话,那就好了。
最后,显然我需要一组决策来确定是否应该应用viewAccount。但我是否正确地认为这意味着我需要为用户可能属于的每个旧组都需要一个策略?例如,如果我有一个"帮助台"角色,那么我需要一个"帮助台成员资格"策略,然后将其添加到"viewAccount"权限中。这样正确吗?

56
Keycloak看起来是一个非常成熟和功能强大的系统,但它到底能做什么仍然是个谜团,因为有太多问题而缺乏答案。我自己也在像你的帖子中提出所有这些问题,却无法找到任何答案。为什么没有好的教程呢?难道没有人真正使用这个东西吗?还是没有人愿意写关于它的文章呢? - Stijn de Witt
7
目前,Keycloak在我们的生产环境中表现良好(至少目前为止),除了授权问题比较难与我的实际问题相关联。但我同意,有很多有关Keycloak如何使用OIDC的文档,但同时也普遍假设我们已经知道OAuth和OIDC。如果你不知道OIDC,就很难将Keycloak与应用程序问题联系起来,但对我来说,Keycloak是OIDC的介绍,这有点像一个恶性循环。(Picketlink/Picketbox甚至更糟!)我发现最好的方法是下载并尝试操作。 - Doctor Eval
16
我同意这些评论,Keycloak 的文档和使用案例确实很糟糕。 - Olivier Refalo
41
Keycloak的开发人员,请注意此问题!您的文档已经很好了,但它需要更多教程来解决这里提出的问题。您还可以考虑迁移到一些更加用户友好的东西,如论坛或仅使用Stackoverflow,而不是老式的邮件列表。 - GGGforce
5
晚回复,但你的所有假设基本上是正确的。至于什么是最佳实践,我认为很难说,因为这项能力非常新颖。甚至不确定kc开发人员在此时是否知道什么是最佳实践。 - cen
显示剩余9条评论
4个回答

384

完全透明度 - 我绝不是Keycloak,OAuthOIDC专家,我所知道的大多来自阅读文档、书籍、好老YouTube和玩弄工具。

本文将由两部分组成:

  1. 我将尽力回答你们所有的问题
  2. 我将向您展示如何在Keycloak中玩弄策略/范围/权限,而无需部署单独的应用程序,以更好地理解本主题中的一些核心概念。请注意,这主要是为了让您们入门。我正在使用Keycloak 8.0.0

第一部分

开始之前先介绍一些术语:

在Keycloak中,您可以创建两种类型的权限:基于资源基于范围。 简单来说,对于基于资源的权限,您直接将其应用于您的资源。 对于基于范围的权限,您将其应用于您的范围或范围资源。
“在多个资源(帐户、交易等)之间使用一个“视图”范围并且重复使用它是最佳实践吗?还是应该创建“viewAccount”范围,“viewTransaction”范围等?” 范围表示受保护资源的一组权限。 在您的情况下,您有两个资源:帐户交易,因此我会倾向于采取第二种方法。
从长远来看,将全局“视图”范围与所有资源(例如“账户”,“交易”,“客户”,“结算”等)相关联,使得授权难以管理和适应安全要求的变化。
以下是一些示例,您可以查看以了解设计感: 请注意 - 我并不是在说您不应该跨资源共享范围。事实上,“Keycloak”允许具有相同类型的资源进行此操作。例如,您可能需要“viewAccount”和“viewTransaction”范围来读取给定帐户下的交易(毕竟您可能需要访问帐户才能查看交易)。您的要求和标准将严重影响您的设计。
针对每种资源和范围的实际组合,通常做法是创建一条许可吗?
抱歉,我不完全理解这个问题,所以我会比较宽泛地回答。为了允许/拒绝访问某个资源,您需要:
  • 定义您的策略
  • 定义您的许可
  • 将您的策略应用于您的许可
  • 将您的许可关联到一个范围资源(或两者都有)
才能生效进行策略执行。请参见授权过程
如何设置所有这些完全取决于您自己。例如,您可以:
  • 定义独立策略,并将每个策略与适当的权限关联。
  • 更好的方法是,定义独立策略,然后将所有相关策略分组到一个“聚合”策略(一种“策略的策略”)下,然后将该聚合策略与基于范围的权限相关联。您可以让这种基于范围的权限适用于资源和其所有关联范围。
  • 或者,您可以通过利用两种不同的类型进一步分解权限。您可以仅通过基于资源的权限类型为资源创建权限,并将其他权限与基于范围的权限类型分开关联。

您有多种选择。

如果有多个权限匹配给定的资源/范围,Keycloak会怎么做?

这取决于

  1. 资源服务器的“决策策略”
  2. 每个权限的“决策策略”
  3. 每个策略的“逻辑”值。

Logic的值类似于Java的!运算符。它可以是PositiveNegative。当LogicPositive时,策略的最终评估保持不变。当LogicNegative时,最终结果被取反(例如,如果一个策略评估为false并且其LogicNegative,那么它将变为true)。为了简单起见,让我们假设Logic始终设置为Positive

真正想要解决的是Decision StrategyDecision Strategy可以是UnanimousAffirmative。根据文档,

决策策略

这些配置更改了策略评估引擎如何决定是否基于所有已评估权限的结果授予资源或范围。 "肯定" 意味着至少一个权限必须评估为积极决策,才能授予对资源及其范围的访问权限。 "一致" 意味着所有权限必须评估为积极决策,才能使最终决策也是积极的。例如,如果同一资源或范围的两个权限存在冲突(其中一个授予访问权限,另一个拒绝访问权限),则如果所选策略是 "肯定",则将授予对资源或范围的许可。否则,任何权限的单个拒绝也将拒绝对资源或范围的访问。
让我们使用一个例子来更好地理解上述内容。假设您有一个资源和两个权限,并且有人正在尝试访问该资源(请记住,所有策略的逻辑都是积极的)。现在:
1. "权限一" 的 "决策策略" 设置为 "肯定"。它还有 3 个策略,每个策略的评估结果如下:
  • true

  • false

  • false

    由于其中一个策略设置为true,因此Permission One被设置为true(肯定 - 只需要有1个是true)。

  1. Permission Two决策策略设置为Unanimous,有两个策略:
  • true

  • false

    在这种情况下,由于一个策略为false(全体一致 - 所有策略都需要是true),所以Permission Twofalse

现在进行最终评估。如果资源服务器的决策策略设置为Affirmative,则会授予对该资源的访问权限,因为权限一true。另一方面,如果资源服务器的决策策略设置为Unanimous,则将拒绝访问权限。

请参见:

我们将一直回顾这个问题。我将在第二部分解释如何设置资源服务器的决策策略

例如,我可以获得访问“账户”和“查看”范围的权限,因此我有权查看账户?

简短的回答是是的。现在,让我们详细了解一下 :)

如果您有以下情况:

  1. 资源服务器的 决策策略 设置为 UnanimousAffirmative
  2. 访问 account/{id} 资源的权限为 true
  3. 访问 view 范围的权限为 true

您将被授予查看账户的访问权限。

  • AffirmativeUnanimous决策策略 下,true + true 等于 true

现在如果您有以下内容

  1. 资源服务器的 决策策略 设置为 Affirmative
  2. 访问 account/{id} 资源的权限为 true
  3. 访问 view 范围的权限为 false

您同样将被授予查看账户的访问权限。

  • true + false肯定策略下为true

这里的重点是,访问给定资源还取决于您的设置,因此要小心,因为您可能不想要第二种情况。

但我是否正确地理解了这意味着我需要为用户可能属于的每个旧组创建一个策略?

我不确定两年前Keycloak的行为如何,但您可以指定基于组的策略,然后将所有组添加到该策略下。您确实不需要为每个组创建一个策略。

例如,如果我有"help desk"角色,那么我需要一个"help desk membership"策略,然后我可以将其添加到"viewAccount"权限中。这正确吗?

差不多。您可以以许多方式设置此项。例如,您可以:

  1. 创建您的资源(例如/account/{id}),并将其与account:view范围关联。
  2. 创建一个基于角色的策略,并在该策略下添加helpdesk角色
  3. 创建一个名为viewAccountScope-Based权限,并将其与scoperesourcepolicy绑定

我们会在第二部分设置类似的内容。

第 II 部分

Keycloak有一个很好的小工具,可以让您测试所有策略。更好的是,您实际上不需要再启动另一个应用程序服务器并部署单独的应用程序才能使其正常工作。

这是我们要设置的方案:

  1. 我们将创建一个名为 stackoverflow-demo 的新领域。
  2. 在该领域下,我们将创建一个名为 bank-api 的客户端。
  3. 我们将为该客户端定义一个名为 /account/{id} 的资源。
  4. account/{id} 将具有 account:view 范围。
  5. 我们将在新领域下创建一个名为 bob 的用户。
  6. 我们还将创建三个角色:bank_telleraccount_owneruser
  • 我们不会将 bob 与任何角色关联起来。目前不需要这样做。
  • 我们将设置以下两个基于角色的策略:
    • bank_telleraccount_owner可以访问/account/{id}资源
    • account_owner可以访问account:view范围
    • user无法访问资源或范围
  • 我们将使用Evaluate工具来测试如何授予或拒绝访问权限。
  • 请原谅我,这个例子并不现实,但我不熟悉银行业务 :)

    Keycloak设置

    下载并运行Keycloak

    cd tmp
    wget https://downloads.jboss.org/keycloak/8.0.0/keycloak-8.0.0.zip
    unzip keycloak-8.0.0.zip
    cd keycloak-8.0.0/bin
    ./standalone.sh
    

    创建初始管理员用户

    1. 访问 http://localhost:8080/auth
    2. 点击 管理控制台 链接
    3. 创建管理员用户并登录

    欲了解更多信息,请访问 入门指南。对于我们的目的,以上内容已足够。

    设置舞台

    创建新领域

    1. 将鼠标悬停在 master 领域上,然后单击 添加领域 按钮。
    2. 输入 stackoverflow-demo 作为名称。
    3. 单击 创建
    4. 左上角现在应显示 stackoverflow-demo 而不是 master 领域。

    请参阅 创建新领域

    创建新用户

    点击左侧的用户链接 点击添加用户按钮 输入用户名(例如bob) 确保启用用户已打开 点击保存 请参见创建新用户

    创建新角色

    点击角色链接 点击添加角色 添加以下角色:bank_telleraccount_owneruser 同样,不要将您的用户与角色关联。对于我们的目的,这是不必要的。
    请参见角色

    创建客户端

    1. 点击 Clients 链接
    2. 点击 Create
    3. Client ID 中输入 bank-api
    4. Root URL 中输入 http://127.0.0.1:8080/bank-api
    5. 点击 Save
    6. 确保 Client Protocol 设置为 openid-connect
    7. Access Type 更改为 confidential
    8. Authorization Enabled 更改为 On
    9. 向下滚动并点击 Save。新的 Authorization 选项卡应该出现在顶部。
    10. 点击 Authorization 选项卡,然后点击 Settings
    11. 确保 Decision Strategy 设置为 Unanimous
    • 这是资源服务器的 Decision Strategy

    参见:

    创建自定义范围

    1. 单击“授权”选项卡
    2. 单击“授权范围”>“创建”以打开“添加范围”页面
    3. 在名称中输入“account:view”,然后按Enter键。

    创建“查看账户资源”

    1. 点击上方的授权链接
    2. 点击资源
    3. 点击创建
    4. 名称显示名称中输入查看账户资源
    5. URI中输入account/{id}
    6. 作用域文本框中输入account:view
    7. 点击保存

    请参见创建资源

    创建您的策略

    再次在 Authorization 标签下,点击 PoliciesCreate Policy 下拉菜单中选择 RoleName 部分输入 Only Bank Teller and Account Owner PolicyRealm Roles 中选择 bank_telleraccount_owner 角色
    确保 Logic 设置为 Positive 点击 Save 点击链接中的 Policies 再次从 Create Policy 下拉菜单中选择 Role 这次使用 Only Account Owner Policy 作为 NameRealm Roles 中选择 account_owner 角色
    确保 Logic 设置为 Positive 点击 Save 点击顶部的 Policies 链接,您现在应该能看到您新创建的策略。

    查看基于角色的策略

    请注意,Keycloak拥有更强大的策略功能。请查看管理策略

    创建基于资源的权限

    1. 再次在授权选项卡下,单击权限
    2. 选择基于资源
    3. 名称键入查看帐户资源权限
    4. 资源下选择查看帐户资源
    5. 应用策略下选择只有银行出纳和账户所有者策略
    6. 确保决策策略设置为一致
    7. 单击保存

    请查看创建基于资源的权限

    咻...

    评估基于资源的权限

    1. 再次在 授权 选项卡下,选择 评估

    2. 用户 下输入 bob

    3. 角色 下选择 user

      • 这是我们将用户与创建的角色关联的地方。
    4. 资源 下选择 查看账户资源 并点击 添加

    5. 点击评估。

    6. 展开 带有作用域 [account:view] 的查看账户资源 以查看结果,您应该会看到 DENY

      Enter image description here

    7. 这很有道理,因为我们只允许两个角色通过 Only Bank Teller and Account Owner Policy 访问该资源。让我们测试一下,确保这是正确的!

    8. 点击评估结果上方的 返回 链接

    9. 将 bob 的角色更改为 account_owner,然后点击 评估。现在您应该看到结果为 PERMIT。如果您返回并将角色更改为 bank_teller,则情况也是一样的。

    查看评估和测试策略

    创建基于范围的权限

    1. 返回到权限部分
    2. 这次在创建权限下拉菜单中选择基于范围
    3. 名称下输入查看账户范围权限
    4. 范围下输入account:view
    5. 应用策略下输入仅限账户所有者策略
    6. 确保决策策略设置为一致
    7. 点击保存

    查看创建基于范围的权限

    第二次测试运行

    评估我们的新更改

    1. 返回到授权部分
    2. 点击评估
    3. 用户应该是bob
    4. 角色应该是bank_teller
    5. 资源应该是查看账户资源,然后点击添加
    6. 点击评估,我们应该得到DENY
      • 这应该不会让人感到惊讶,因为bank_teller可以访问资源但不能访问范围。在这里,一个权限计算结果为true,另一个为false。鉴于资源服务器的决策策略设置为Unanimous,最终决策是DENY
    7. 授权选项卡下点击设置,将决策策略更改为Affirmative,然后再次执行步骤1-6。这次,最终结果应该是PERMIT(一个权限为true,所以最终决策为true)。
    8. 为了完整起见,将资源服务器的决策策略改回Unanimous。再次执行步骤1到6,但这次将角色设置为account_owner。这次,最终结果仍然是PERMIT,这是有道理的,因为account_owner可以访问资源范围

    4
    @JWo 不客气!说实话,没有特别的理由。我只是尽可能地保持了这个例子的简单性,以便让任何人都能够入门。根据我的经验,文档并不是最令人兴奋的阅读材料。至于另一个客户需要访问银行API的问题,听起来你可能正在寻找“用户管理访问(UMA)”,其中资源所有者可以授予请求方对受保护资源的访问权限(至少这是我的解释)。 - Andy
    21
    你确实正在让世界变得更美好,感谢你抽出时间来整理所有这些内容...你应该写一些关于这一切的博客文章!非常有用。@Andy - José Carlos
    2
    @Andy,太厉害了!感谢你花时间消化所有Auth Services文档并与我们分享。你为许多人省下了很多学习时间!只有一件事我没看到。你说account_owner可以访问账户:view范围。那个角色和范围之间的关系是如何建立的? - codependent
    2
    @codependent 不用谢 :) 回想起来,我应该表述得更好一些。最简单的方法是按照设置步骤进行,但总之,在您关联角色和策略之后,您需要创建基于范围的权限,然后将您的策略应用于该权限。在上面的示例中,您有“仅账户所有者策略”,它与“account_owner”角色相关联。然后将该策略应用于“View Account Scope Permission”。在我们创建该权限时,我们将其与“account:view”范围相关联。希望这可以帮助您! - Andy
    1
    这样美丽而深思熟虑的答案需要更多的认可和赞赏。@Andy,我再怎么感激你都不为过。谢谢,谢谢! - Arthur
    显示剩余11条评论

    32

    我知道我来晚了,但让我尽可能地解释一下。

    在Keycloak中,我们有以下术语:

    资源:用户将访问或执行操作的对象

    授权范围:用户可以在特定对象上执行的操作

    策略:策略

    权限:映射实际发生在此处

    如果您不想按照手动方式操作,可以导出此JSON,所有用户、资源和权限都将由Keycloak自动设置

    JSON配置文件

    现在让我们看一个场景:

    Enter image description here

    现在我们有一些资源,例如:

    1. 账户
    2. 机器人
    3. 报告

    我们想要实现这样的场景:只有特定用户才能执行特定的操作。

    设置 Keycloak

    创建新的领域

    1. 点击添加领域按钮。

      Enter image description here

    2. 将名称输入为test-v1

    3. 点击“创建”按钮。

      Enter image description here

    创建新角色

    1. 点击角色

    2. 点击添加角色

    3. 创建角色 "admin", "agent" 和 "super_admin"

      Enter image description here

    创建客户端

    • 点击Clients选项卡

    • 在客户端ID文本框中输入app-client

    • 点击保存

    • 再次选择并选择客户端以配置其他设置

    • 验证客户端协议为openid-connect

    • 将访问类型设置为机密

    • 将授权启用设置为开启

    • 最后,点击保存按钮。

    • 顶部将出现一个新的授权选项卡。

    • 选择授权选项卡,然后选择设置

      检查决策策略是否设置为一致。这是资源服务器策略

      Enter image description here

    创建自定义身份验证范围

    进入授权选项卡 选择授权范围→并单击创建 在文本框中输入scopes:createscopes:view,然后保存值。

    Enter image description here

    创建资源

    • 现在前往资源选项卡 → 并单击创建

    • 逐个输入并创建以下资源res:account & res:bot & res:report

    • 对于所有范围内的资源,请选择我们早期创建的两个范围scopes:create & scopes:view

    • 单击保存

      Enter image description here

    创建策略

    • 再次进入授权选项卡,在策略中选择

    • 单击创建策略下拉菜单,选择角色

    • 在名称文本框中输入Admin

    • 在领域角色中选择角色Admin

    • 检查逻辑是否设置为正面

    • 单击保存,并对“Agent”和“Super_admin”执行相同操作

      Enter image description here

    • 再次进入授权选项卡,在策略中选择

    • 单击创建策略下拉菜单,选择聚合

    • 在名称文本框中输入Admin or Super_admin or Agent

    • 在领域角色中选择角色Admin & Super_admin & Agent

    • 检查逻辑是否设置为肯定

    • 单击保存

      Enter image description here

    创建权限

    • 再次进入授权选项卡,选择权限

    • 点击创建权限下拉菜单,选择基于范围

    • 在名称文本框中输入account-create

    • 在资源框中选择“资源res:account”

    • 在作用域中选择scopes:create

    • 应用策略管理员

    • 我们必须根据要求为所有资源设置相同的权限

      Enter image description here

    创建用户

    • 在用户选项卡内创建一个测试用户 我们将不会为其分配任何角色、作用域或组进行测试

    让我们评估一下

    • 再次进入授权选项卡,在 Evaluate 上选择

    • 选择我们创建的客户端,app-client

    • 在用户中选择创建的用户,test

    • 在角色中选择创建的用户,admin

    • 资源值,res:account

    • 点击 Add 按钮

    • 点击 Evaluate 按钮

      Enter image description here

      您将看到授权已被允许,因为 Admin 角色可以在资源 account 上执行 createview 操作。

    让我们再次评估

    • 再次进入授权选项卡,在 Evaluate 上选择

    • 选择我们创建的客户端,app-client

    • 在用户中选择创建的用户,test

    • 在角色中选择创建的用户,admin

    • 资源值,res:report

    • 范围值,scopes:create

    • 点击 Add 按钮

    • 点击 Evaluate 按钮

      Enter image description here

    您会看到授权被拒绝,因为只有 Super_Admin 角色才能访问资源 report 上的操作 create


    2
    非常感谢,它很有帮助!我有一个问题,如果您不介意的话,我能否使用API将其添加到Keycloak的代码源中以连接我的应用程序,或者我只需要使用身份验证提供者? - ingmbk
    1
    很高兴听到这个消息,抱歉没有理解你的问题。但请随意提供更详细的信息。仍在努力回答:您的应用程序将通过JSON文件配置连接到Keycloak,类似于:https://github.com/v-ladynev/keycloak-nodejs-example/blob/master/keycloak.json 这个完整的示例是使用Node.js编写的,非常好,您可以一试。 - Harsh Manvar
    嗨@HarshManvar,这适用于微服务吗?我已经尝试了您的建议,使用了两个页面(A&B)。 A&B是两个服务,使用keycloak进行身份验证以及OAuth。我将它们添加到“验证重定向URL”中,但似乎不起作用。 - tandathuynh148

    11
    我希望您能通过纯HTTP方法执行授权,而不使用适配器,因为Lua没有适配器。希望这个答案可以帮助寻找非适配器方法的人。
    如果您正在寻找适配器,则快速入门指南是最好的起点,特别是spring boot authz example
    对于基于纯HTTP的实现: 步骤1:Keycloak Admin UI中定义策略和权限。 步骤2: 内部映射哪些HTTP路径属于哪些资源以及每个路径所需的范围。这也可以保存在配置文件中。当调用特定路由时,请调用Keycloak令牌端点来验证访问令牌的声明。
    {
      "policy-enforcer": {
        "user-managed-access" : {},
        "enforcement-mode" : "ENFORCING"
        "paths": [
          {
            "path" : "/someUri/*",
            "methods" : [
              {
                "method": "GET",
                "scopes" : ["urn:app.com:scopes:view"]
              },
              {
                "method": "POST",
                "scopes" : ["urn:app.com:scopes:create"]
              }
            ]
          }
        ]
      }
    }
    

    如果您使用适配器并且没有指定路径或资源,则适配器将在内部搜索来自Keycloak的路径和资源。
    第三步:
    使用令牌端点获取或评估权限。您可以使用response_mode参数来获取最终决策(是否提供访问权限)或检索整个权限。
    curl -X POST \
      http://${host}:${port}/auth/realms/${realm}/protocol/openid-connect/token \
      -H "Authorization: Bearer ${access_token}" \
      --data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" \
      --data "permission=Resource A#Scope A"
    

    如果授权请求没有映射到任何权限,则返回403 HTTP状态代码。

    3
    可以在这里找到具有资源、范围和权限的可工作解决方案:keycloak-nodejs-example。只需按照快速入门指南使用docker-compose运行预配置的Keycloak即可。
    项目中的其他有用示例包括:
    - 自定义登录页面,无需使用Keycloak的登录页面。 - 无状态Node.js服务器,不使用会话。Keycloak令牌存储在cookie中。 - 集中式中间件以检查权限。未明确描述路由的路由无法访问。 - 不使用keycloak.json的配置。可以用于多个环境的配置,例如DEV、QA。 - 使用Keycloak REST API创建用户、角色和自定义属性的示例。

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接