授权服务概述

Keycloak 支持细粒度授权策略,并且能够结合不同的访问控制机制,例如

  • 基于属性的访问控制 (ABAC)

  • 基于角色的访问控制 (RBAC)

  • 基于用户的访问控制 (UBAC)

  • 基于上下文的访问控制 (CBAC)

  • 基于规则的访问控制

    • 使用 JavaScript

  • 基于时间的访问控制

  • 通过服务提供者接口 (SPI) 支持自定义访问控制机制 (ACM)

Keycloak 基于一组管理 UI 和 RESTful API,并提供必要的工具来创建受保护资源和范围的权限,将这些权限与授权策略相关联,并在您的应用程序和服务中执行授权决策。

资源服务器(服务受保护资源的应用程序或服务)通常依赖于某种信息来决定是否应授予受保护资源的访问权限。对于基于 RESTful 的资源服务器,该信息通常从安全令牌(通常作为承载令牌发送到服务器的每个请求中)获取。对于依赖会话来验证用户的 Web 应用程序,该信息通常存储在用户的会话中,并在每个请求中从那里检索。

通常,资源服务器仅根据基于角色的访问控制 (RBAC) 执行授权决策,其中授予尝试访问受保护资源的用户的角色将与映射到这些相同资源的角色进行检查。虽然角色非常有用,并且被应用程序使用,但它们也有一些限制

  • 资源和角色紧密耦合,角色的更改(例如添加、删除或更改访问上下文)会影响多个资源

  • 对安全要求的更改可能意味着对应用程序代码进行深入更改以反映这些更改

  • 根据应用程序的大小,角色管理可能会变得困难且容易出错

  • 它不是最灵活的访问控制机制。角色不能代表你是谁,并且缺乏上下文信息。如果您已被授予角色,那么您至少拥有某种访问权限。

考虑到如今我们需要考虑异构环境,其中用户分布在不同的区域,具有不同的本地策略,使用不同的设备,并且对信息共享的需求很高,Keycloak 授权服务可以帮助您通过提供以下功能来提高应用程序和服务的授权能力

  • 使用细粒度授权策略和不同的访问控制机制保护资源

  • 集中式资源、权限和策略管理

  • 集中式策略决策点

  • 基于一组基于 REST 的授权服务的 REST 安全

  • 授权工作流程和用户管理的访问

  • 帮助避免跨项目(和重新部署)的代码复制,并快速适应安全要求变化的基础设施。

架构

Keycloak AuthZ architecture overview

从设计角度来看,授权服务基于一组定义明确的授权模式,提供以下功能

  • 策略管理点 (PAP)

    提供一组基于 Keycloak 管理控制台的 UI 来管理资源服务器、资源、范围、权限和策略。部分内容也是通过使用 保护 API 远程完成的。

  • 策略决策点 (PDP)

    提供一个可分发的策略决策点,将授权请求发送到该点,并根据请求的权限相应地评估策略。有关更多信息,请参阅 获取权限

  • 策略执行点 (PEP)

    为不同的环境提供实现,以实际在资源服务器端执行授权决策。Keycloak 提供了一些内置的 策略执行者

  • 策略信息点 (PIP)

    基于 Keycloak 身份验证服务器,您可以在评估授权策略期间从身份和运行时环境中获取属性。

授权流程

三个主要流程定义了理解如何使用 Keycloak 为您的应用程序启用细粒度授权的必要步骤

  • 资源管理

  • 权限和策略管理

  • 策略执行

资源管理

资源管理 包括定义要保护内容的所有必要步骤。

Resource management overview

首先,您需要指定 Keycloak 要保护的内容,这通常代表一个 Web 应用程序或一组一个或多个服务。有关资源服务器的更多信息,请参阅 术语

资源服务器使用 Keycloak 管理控制台进行管理。在那里,您可以将任何注册的客户端应用程序启用为资源服务器,并开始管理要保护的资源和范围。

Resource Server overview

资源可以是网页、RESTful 资源、文件系统中的文件、EJB 等等。它们可以表示一组资源(就像 Java 中的类一样),也可以表示单个特定的资源。

例如,您可能有一个 银行账户 资源,它代表所有银行账户,并使用它来定义对所有银行账户通用的授权策略。但是,您可能希望为 Alice 账户(属于客户的资源实例)定义特定的策略,其中只有所有者才能访问某些信息或执行操作。

资源可以使用 Keycloak 管理控制台或 保护 API 进行管理。在后一种情况下,资源服务器能够远程管理其资源。

范围通常表示可以在资源上执行的操作,但并不限于此。您还可以使用范围来表示资源中的一个或多个属性。

权限和策略管理

定义了资源服务器和要保护的所有资源后,您必须设置权限和策略。

此过程涉及实际定义控制资源的安全性和访问要求的所有必要步骤。

Permission and policy management overview

策略定义访问或对某些内容(资源或范围)执行操作时必须满足的条件,但它们与要保护的内容无关。它们是通用的,可以重复使用来构建权限甚至更复杂的策略。

例如,要仅允许授予 "用户高级" 角色的用户访问一组资源,您可以使用 RBAC(基于角色的访问控制)。

Keycloak 提供了一些内置的策略类型(及其相应的策略提供者),涵盖最常见的访问控制机制。您甚至可以创建基于使用 JavaScript 编写的规则的策略。

定义好策略后,就可以开始定义权限。权限与他们正在保护的资源耦合。在这里,您指定要保护的内容(资源或范围)以及必须满足哪些策略才能授予或拒绝权限。

策略执行

策略执行 包括实际对资源服务器执行授权决策的所有必要步骤。这是通过在能够与授权服务器通信、请求授权数据并根据服务器返回的决策和权限控制对受保护资源的访问的资源服务器上启用 策略执行点 或 PEP 来实现的。

PEP overview

Keycloak 提供了一些内置的 策略执行者 实现,您可以使用它们来保护您的应用程序,具体取决于它们运行的平台。

授权服务

授权服务由以下 RESTful 端点组成

  • 令牌端点

  • 资源管理端点

  • 权限管理端点

每个服务都提供一个特定的 API,涵盖授权流程中涉及的不同步骤。

令牌端点

OAuth2 客户端(如前端应用程序)可以通过令牌端点从服务器获取访问令牌,并使用这些相同的令牌访问由资源服务器(如后端服务)保护的资源。同样,Keycloak 授权服务为 OAuth2 提供扩展,以允许根据与请求的资源或范围关联的所有策略的处理来颁发访问令牌。这意味着资源服务器可以根据服务器授予的权限以及访问令牌持有的权限来强制执行对其受保护资源的访问。在 Keycloak 授权服务中,具有权限的访问令牌称为请求方令牌或简称 RPT。

其他资源
保护 API

保护 API 是一组 符合 UMA 的端点提供操作,用于帮助资源服务器管理其资源、范围、权限以及与其关联的策略。只有资源服务器才能访问此 API,这还需要 uma_protection 范围。

保护 API 提供的操作可以分为两大类

  • 资源管理

    • 创建资源

    • 删除资源

    • 按 ID 查找

    • 查询

  • 权限管理

    • 颁发权限票证

默认情况下,远程资源管理已启用。您可以使用 Keycloak 管理控制台更改它,并且只允许通过控制台进行资源管理。

使用 UMA 协议时,保护 API 颁发权限票证是整个授权流程的重要部分。如后续部分所述,它们代表客户端请求的权限,这些权限被发送到服务器以获取最终令牌,该令牌包含在评估与请求的资源和范围关联的权限和策略期间授予的所有权限。

其他资源

术语

在继续之前,了解 Keycloak 授权服务引入的这些术语和概念非常重要。

资源服务器

根据 OAuth2 术语,资源服务器是托管受保护资源的服务器,能够接受和响应受保护资源请求。

资源服务器通常依赖于某种信息来决定是否应该授予对受保护资源的访问权限。对于基于 RESTful 的资源服务器,该信息通常存储在安全令牌中,通常作为承载令牌与每个对服务器的请求一起发送。依赖于会话来验证用户的 Web 应用程序通常将该信息存储在用户的会话中,并在每次请求时从那里检索它。

在 Keycloak 中,任何 机密 客户端应用程序都可以充当资源服务器。此客户端的资源及其各自的范围受到一组授权策略的保护和管理。

资源

资源是应用程序和组织的资产的一部分。它可以是一组一个或多个端点、一个经典的 Web 资源(如 HTML 页面)等等。在授权策略术语中,资源是正在保护的 对象

每个资源都有一个唯一的标识符,可以代表单个资源或一组资源。例如,您可以管理一个 银行账户资源,它代表并定义了一组适用于所有银行账户的授权策略。但您也可以拥有一个名为 爱丽丝的银行账户 的不同资源,它代表单个客户拥有的单个资源,它可以拥有自己的授权策略集。

范围

资源的范围是对资源可以执行的访问的有限程度。在授权策略术语中,范围是可能在逻辑上应用于资源的众多 动词 之一。

它通常表示可以使用给定资源做什么。范围的示例有查看、编辑、删除等等。但是,范围也可以与资源提供的特定信息相关。在这种情况下,您可以拥有一个项目资源和一个成本范围,其中成本范围用于定义用户访问项目成本的特定策略和权限。

权限

考虑这个简单且非常常见的权限

权限将正在保护的对象与必须评估以确定是否授予访问权限的策略相关联。

  • X 可以对资源 Z 执行 Y

    • 其中……

      • X 代表一个或多个用户、角色或组,或它们的组合。您也可以在这里使用声明和上下文。

      • Y 代表要执行的操作,例如,写入、查看等等。

      • Z 代表受保护的资源,例如,"/accounts"。

Keycloak 提供了一个丰富的平台来构建从简单到非常复杂的基于规则的动态权限的各种权限策略。它提供了灵活性,并有助于

  • 减少代码重构和权限管理成本

  • 支持更灵活的安全模型,帮助您轻松适应安全需求的变化

  • 在运行时进行更改;应用程序只关心受保护的资源和范围,而不关心它们是如何受保护的。

策略

策略定义了必须满足的条件才能授予对对象的访问权限。与权限不同,您不会指定正在保护的对象,而是指定必须满足的条件才能访问给定对象(例如,资源、范围或两者)。策略与您可以用来保护资源的不同访问控制机制 (ACM) 密切相关。使用策略,您可以实现基于属性的访问控制 (ABAC)、基于角色的访问控制 (RBAC)、基于上下文的访问控制或这些方法的任何组合的策略。

Keycloak 利用策略的概念以及您如何通过提供聚合策略的概念来定义它们,您可以在其中构建“策略的策略”,并且仍然控制评估的行为。与其编写一个包含必须满足的所有条件才能访问给定资源的大型策略,Keycloak 授权服务中的策略实现遵循分而治之的技术。也就是说,您可以创建单独的策略,然后将它们与不同的权限一起重用,并通过组合单独的策略来构建更复杂的策略。

策略提供者

策略提供者是特定策略类型的实现。Keycloak 提供了内置策略,由其相应的策略提供者支持,您可以创建自己的策略类型来支持您的特定需求。

Keycloak 提供了一个 SPI(服务提供者接口),您可以使用它来插入您自己的策略提供者实现。

权限票证

权限票证是用户管理访问 (UMA) 规范定义的一种特殊类型的令牌,它提供了一个不透明的结构,其形式由授权服务器决定。此结构表示客户端请求的资源和/或范围、访问上下文以及必须应用于授权数据(请求方令牌 [RPT])请求的策略。

在 UMA 中,权限票证对于支持人与人共享以及人与组织共享至关重要。使用权限票证进行授权工作流程可以实现从简单到复杂的各种场景,其中资源所有者和资源服务器根据管理对这些资源的访问的细粒度策略完全控制其资源。

在 UMA 工作流程中,权限票证由授权服务器颁发给资源服务器,资源服务器将权限票证返回给尝试访问受保护资源的客户端。客户端收到票证后,可以通过将票证发送回授权服务器来请求 RPT(包含授权数据的最终令牌)。

有关权限票证的更多信息,请参阅 用户管理访问 以及 UMA 规范。

入门

对于某些应用程序,您可以查看以下资源以快速开始使用 Keycloak 授权服务

管理资源服务器

根据 OAuth2 规范,资源服务器是托管受保护资源的服务器,能够接受和响应受保护资源请求。

在 Keycloak 中,资源服务器提供了一个丰富的平台,用于为其受保护资源启用细粒度授权,其中授权决策可以基于不同的访问控制机制。

任何客户端应用程序都可以配置为支持细粒度权限。这样做,您在概念上将客户端应用程序转换为资源服务器。

创建客户端应用程序

启用 Keycloak 授权服务的第一个步骤是创建要转换为资源服务器的客户端应用程序。

步骤
  1. 单击 客户端

    客户端

    Clients

  2. 在此页面上,单击 创建客户端

    添加客户端

    Add Client

  3. 键入客户端的 客户端 ID。例如,my-resource-server

  4. 单击 下一步

  5. 客户端身份验证 切换到 ON。

  6. 单击 保存

  7. 键入应用程序的 根 URL。例如

    http://${host}:${port}/my-resource-server
  8. 单击 保存。客户端将被创建,并且客户端设置页面将打开。将显示类似于以下内容的页面

    客户端设置

    Client Settings

启用授权服务

您可以将您的 OIDC 客户端变成资源服务器,并启用细粒度授权。

步骤
  1. 在客户端设置页面中,向下滚动到 **功能配置** 部分。

  2. 将 **授权启用** 切换到 **开启**。

  3. 单击 保存

    启用授权服务

    Enabling authorization services

    此客户端将显示一个新的 **授权** 选项卡。点击 **授权** 选项卡,您将看到类似于以下内容的页面

    资源服务器设置

    Resource server settings

**授权** 选项卡包含其他子选项卡,涵盖了您实际保护应用程序资源必须遵循的不同步骤。每个选项卡在本文档中由特定主题单独介绍。但这里对每个选项卡做一个简要描述

  • 设置

    资源服务器的常规设置。有关此页面的更多详细信息,请参阅 资源服务器设置 部分。

  • 资源

    从该页面,您可以管理应用程序的 资源

  • 授权范围

    从该页面,您可以管理 范围

  • 策略

    从该页面,您可以管理 授权策略,并定义授予权限必须满足的条件。

  • 权限

    从该页面,您可以通过将您创建的策略链接到受保护的资源和范围,来管理 权限

  • 评估

    从该页面,您可以 模拟授权请求,并查看您定义的权限和授权策略评估结果。

  • 导出设置

    从该页面,您可以 导出 授权设置到 JSON 文件。

资源服务器设置

在资源服务器设置页面,您可以配置策略执行模式,允许远程资源管理,并导出授权配置设置。

  • 策略执行模式

    指定在处理发送到服务器的授权请求时如何执行策略。

    • 强制执行

      (默认模式) 即使给定资源没有关联的策略,请求也会默认被拒绝。

    • 宽松

      即使给定资源没有关联的策略,请求也会被允许。

    • 禁用

      禁用所有策略的评估,并允许访问所有资源。

  • 决策策略

    此配置更改了策略评估引擎根据所有评估权限的结果来决定是否应根据所有评估权限的结果来决定是否应授予资源或范围的访问权限的方式。`肯定` 表示至少一项权限必须评估为肯定的决定,才能授予对资源及其范围的访问权限。`一致` 表示所有权限必须评估为肯定的决定,才能使最终决定也为肯定。例如,如果同一个资源或范围的两个权限发生冲突(其中一个授予访问权限,另一个拒绝访问权限),如果选择的策略是`肯定`,则将授予对资源或范围的访问权限。否则,来自任何权限的单个拒绝也会拒绝对资源或范围的访问权限。

  • 远程资源管理

    指定资源是否可以由资源服务器远程管理。如果为假,则只能从管理控制台管理资源。

默认配置

创建资源服务器时,Keycloak 会为新创建的资源服务器创建默认配置。

默认配置包括

  • 一个表示应用程序中所有资源的默认受保护资源。

  • 始终授予访问受此策略保护的资源的策略。

  • 根据默认策略管理对所有资源的访问权限的权限。

默认受保护资源被称为 **默认资源**,您可以在导航到 **资源** 选项卡时查看它。

默认资源

Default resource

此资源定义了一个 `类型`,即 `urn:my-resource-server:resources:default` 和一个 `URI` `/*`。在这里,`URI` 字段定义了一个通配符模式,表示 Keycloak 此资源代表应用程序中的所有路径。换句话说,在为您的应用程序启用 策略执行 时,将检查与该资源关联的所有权限,然后才能授予访问权限。

之前提到的 `类型` 定义了一个值,该值可用于创建必须应用于默认资源或使用相同类型创建的任何其他资源的 类型资源权限

默认策略被称为 **仅来自领域策略**,您可以在导航到 **策略** 选项卡时查看它。

默认策略

Default policy

此策略是一个 基于 JavaScript 的策略,定义一个始终授予访问受此策略保护的资源的条件。如果您点击此策略,您可以看到它定义了一个规则如下

// by default, grants any permission associated with this policy
$evaluation.grant();

最后,默认权限被称为 **默认权限**,您可以在导航到 **权限** 选项卡时查看它。

默认权限

Default Permission

此权限是一个 基于资源的权限,定义了一组或多组应用于具有给定类型的所有资源的策略。

更改默认配置

您可以通过删除默认资源、策略或权限定义并创建您自己的定义来更改默认配置。

默认资源使用一个映射到应用程序中任何资源或路径的 **URI** 创建,使用 **/** 模式。在创建您自己的资源、权限和策略之前,请确保默认配置不会与您自己的设置冲突。

默认配置定义了一个映射到应用程序中所有路径的资源。如果您要向您自己的资源写入权限,请确保删除 **默认资源** 或将其 `URIS` 字段更改为应用程序中更具体的路径。否则,与默认资源(默认情况下始终授予访问权限)关联的策略将允许 Keycloak 授予对任何受保护资源的访问权限。

导出和导入授权配置

可以导出和下载资源服务器(或客户端)的配置设置。您也可以为资源服务器导入现有的配置文件。当您要为资源服务器创建初始配置或更新现有配置时,导入和导出配置文件非常有用。配置文件包含受保护资源和范围的定义。

  • 受保护资源和范围

  • 策略

  • 权限

导出配置文件

步骤
  1. 在菜单中点击 **客户端**。

  2. 点击您作为资源服务器创建的客户端。

  3. 点击 **导出** 选项卡。

    导出设置

    Export Settings

配置文件以 JSON 格式导出并显示在文本区域中,您可以在其中复制和粘贴。您也可以点击 **下载** 下载配置文件并保存。

导入配置文件

您可以为资源服务器导入配置文件。

步骤
  1. 导航到 **资源服务器设置** 页面。

    导入设置

    Import Settings

  2. 点击 **导入**,然后选择包含要导入的配置的文件。

管理资源和范围

资源管理非常直观和通用。创建资源服务器后,您可以开始创建要保护的资源和范围。可以通过分别导航到 **资源** 和 **授权范围** 选项卡来管理资源和范围。

查看资源

在 **资源** 页面,您将看到与资源服务器关联的资源列表。

资源

Resources

资源列表提供了有关受保护资源的信息,例如

  • 类型

  • URI

  • 所有者

  • 关联的范围(如果有)

  • 关联的权限

从该列表中,您也可以直接创建权限,方法是点击要为其创建权限的资源的 **创建权限**。

在为您的资源创建权限之前,请确保您已经定义了要与权限关联的策略。

创建资源

创建资源很简单,并且通用。您主要需要考虑的是创建资源的粒度。换句话说,可以创建资源来表示一组一个或多个资源,而您定义它们的方式对于管理权限至关重要。

要创建新的资源,请单击**创建资源**。

添加资源

Add resource

在 Keycloak 中,资源定义了一组小的信息,这些信息对于不同类型的资源是通用的,例如

  • 名称

    一个描述此资源的人类可读且唯一的字符串。

  • 类型

    一个唯一标识一组一个或多个资源类型的字符串。类型是一个字符串,用于对不同的资源实例进行分组。例如,自动创建的默认资源的默认类型为urn:resource-server-name:resources:default

  • URI

    提供资源位置/地址的 URI。对于 HTTP 资源,URI 通常是用于提供这些资源的相对路径。

  • 范围

    与资源关联的一个或多个范围。

资源属性

资源可能与之相关联的属性。这些属性可用于提供有关资源的更多信息,并在评估与资源相关的权限时向策略提供更多信息。

每个属性都是一个键值对,其中值可以是一组一个或多个字符串。可以通过逗号分隔每个值来为属性定义多个值。

类型化资源

资源的类型字段可用于将不同的资源分组在一起,以便可以使用一组通用的权限来保护它们。

资源所有者

资源也有所有者。默认情况下,资源由资源服务器拥有。

但是,资源也可以与用户关联,因此您可以根据资源所有者创建权限。例如,只有资源所有者才能删除或更新给定资源。

远程管理资源

资源管理也通过Protection API公开,以允许资源服务器远程管理其资源。

使用 Protection API 时,可以实现资源服务器来管理其用户拥有的资源。在这种情况下,您可以指定用户标识符将资源配置为属于特定用户。

Keycloak 为资源服务器提供了对其资源的完全控制。将来,我们应该能够允许用户控制自己的资源,以及批准授权请求和管理权限,尤其是在使用 UMA 协议时。

管理策略

如前所述,策略定义了在授予对对象的访问权限之前必须满足的条件。

步骤
  1. 单击**策略**选项卡以查看与资源服务器关联的所有策略。

    策略

    Policies

    在此选项卡上,您可以查看以前创建的策略列表,以及创建和编辑策略。

要创建新策略,请单击**创建策略**,然后从列表中选择策略类型。

本节介绍了每种策略类型的详细信息。

基于用户的策略

您可以使用这种类型的策略为您的权限定义条件,其中一组一个或多个用户被允许访问对象。

要创建新的基于用户的策略,请在策略列表右上角的项目列表中选择**用户**。

添加用户策略

Add User Policy

配置

  • 名称

    一个人类可读且唯一的字符串,用于标识策略。最佳实践是使用与您的业务和安全需求密切相关的名称,以便您可以更轻松地识别它们。

  • 描述

    包含有关此策略的详细信息的字符串。

  • 用户

    指定哪些用户可以通过此策略获得访问权限。

  • 逻辑

    在其他条件评估完毕后应用的此策略的逻辑。

其他资源

基于角色的策略

您可以使用这种类型的策略为您的权限定义条件,其中一组一个或多个角色被允许访问对象。

默认情况下,添加到此策略的角色不会被指定为必需,并且如果请求访问的用户已授予这些角色中的任何一个,则策略将授予访问权限。但是,如果您想强制执行特定角色,则可以将特定角色指定为必需。您还可以将必需角色和非必需角色组合在一起,无论它们是 realm 角色还是客户端角色。

当您需要更严格的基于角色的访问控制 (RBAC) 时,角色策略很有用,在这种情况下,必须强制执行特定角色才能授予对对象的访问权限。例如,您可以强制要求用户同意允许客户端应用程序(代表用户行事)访问用户的资源。您可以使用 Keycloak 客户端范围映射来启用同意页面,甚至强制客户端在从 Keycloak 服务器获取访问令牌时明确提供范围。

要创建新的基于角色的策略,请从策略类型列表中选择**角色**。

添加角色策略

Add Role Policy

配置

  • 名称

    一个描述策略的人类可读且唯一的字符串。最佳实践是使用与您的业务和安全需求密切相关的名称,以便您可以更轻松地识别它们。

  • 描述

    包含有关此策略的详细信息的字符串。

  • Realm 角色

    指定此策略允许哪些**realm**角色。

  • 客户端角色

    指定此策略允许哪些**客户端**角色。要启用此字段,必须首先选择一个客户端

  • 获取角色

    默认情况下,只有来自与授权请求一起发送的令牌中可用的角色用于检查用户是否被授予角色。如果启用此设置,策略将忽略令牌中的角色,而是检查与用户关联的任何角色。

  • 逻辑

    在其他条件评估完毕后应用的此策略的逻辑。

其他资源

将角色定义为必需

创建基于角色的策略时,可以将特定角色指定为必需。当您这样做时,只有在请求访问的用户已授予**所有****必需**角色时,策略才会授予访问权限。realm 角色和客户端角色都可以配置为这样。

必需角色示例

Example of a required role

要将角色指定为必需,请选择要配置为必需的角色的必需复选框。

当您的策略定义了多个角色,但其中只有一部分是强制性的时,必需角色很有用。在这种情况下,您可以将 realm 角色和客户端角色组合在一起,为您的应用程序启用更细粒度的基于角色的访问控制 (RBAC) 模型。例如,您可以为客户端设置特定策略,并要求与该客户端关联的特定客户端角色。或者,您可以强制执行只有在存在特定 realm 角色时才能授予访问权限。您也可以在同一策略中将这两种方法结合起来。

基于 JavaScript 的策略

如果您的策略实现使用基于属性的访问控制 (ABAC)(如以下示例所示),那么请确保用户无法编辑受保护的属性,并且相应的属性为只读。有关详细信息,请参阅威胁模型缓解章节。

您可以使用这种类型的策略使用 JavaScript 为您的权限定义条件。它是 Keycloak 支持的基于规则的策略类型之一,并提供灵活性,以便根据评估 API编写任何策略。

要创建新的基于 JavaScript 的策略,请在策略列表右上角的项目列表中选择**JavaScript**。

默认情况下,JavaScript 策略无法上传到服务器。您应该优先考虑将您的 JS 策略直接部署到服务器,如JavaScript 提供者中所述。

从已部署的 JAR 文件创建 JS 策略

Keycloak 允许您部署 JAR 文件以便将脚本部署到服务器。请查看JavaScript 提供者以获取更多详细信息。

部署脚本后,您应该能够从可用的策略提供程序列表中选择部署的脚本。

示例

检查来自评估上下文的属性

以下是一个简单的基于 JavaScript 的策略示例,它使用基于属性的访问控制 (ABAC) 来定义基于从执行上下文中获取的属性的条件

const context = $evaluation.getContext();
const contextAttributes = context.getAttributes();

if (contextAttributes.containsValue('kc.client.network.ip_address', '127.0.0.1')) {
    $evaluation.grant();
}
检查来自当前身份的属性

以下是一个简单的基于 JavaScript 的策略示例,它使用基于属性的访问控制 (ABAC) 来定义基于与当前身份关联的属性的条件

const context = $evaluation.getContext();
const identity = context.getIdentity();
const attributes = identity.getAttributes();
const email = attributes.getValue('email').asString(0);

if (email.endsWith('@keycloak.org')) {
    $evaluation.grant();
}

其中这些属性从在授权请求中使用的令牌中定义的任何声明中映射。

检查授予当前身份的角色

您也可以在策略中使用基于角色的访问控制 (RBAC)。在下面的示例中,我们检查用户是否被授予keycloak_user**领域**角色

const context = $evaluation.getContext();
const identity = context.getIdentity();

if (identity.hasRealmRole('keycloak_user')) {
    $evaluation.grant();
}

或者,您可以检查用户是否被授予my-client-role**客户端**角色,其中my-client是客户端应用程序的客户端 ID

const context = $evaluation.getContext();
const identity = context.getIdentity();

if (identity.hasClientRole('my-client', 'my-client-role')) {
    $evaluation.grant();
}
检查授予用户的角色

要检查授予用户的领域角色

const realm = $evaluation.getRealm();

if (realm.isUserInRealmRole('marta', 'role-a')) {
    $evaluation.grant();
}

或者检查授予用户的客户端角色

const realm = $evaluation.getRealm();

if (realm.isUserInClientRole('marta', 'my-client', 'some-client-role')) {
    $evaluation.grant();
}
检查授予组的角色

要检查授予组的领域角色

const realm = $evaluation.getRealm();

if (realm.isGroupInRole('/Group A/Group D', 'role-a')) {
    $evaluation.grant();
}
将任意声明推送到资源服务器

为了提供有关如何执行权限的更多信息,将任意声明推送到资源服务器

const permission = $evaluation.getPermission();

// decide if permission should be granted

if (granted) {
    permission.addClaim('claim-a', 'claim-a');
    permission.addClaim('claim-a', 'claim-a1');
    permission.addClaim('claim-b', 'claim-b');
}
检查组成员身份
const realm = $evaluation.getRealm();

if (realm.isUserInGroup('marta', '/Group A/Group B')) {
    $evaluation.grant();
}
混合不同的访问控制机制

您还可以使用多种访问控制机制的组合。以下示例显示了如何在同一策略中使用角色 (RBAC) 和声明/属性 (ABAC) 检查。在这种情况下,我们检查用户是否被授予admin角色或是否具有来自keycloak.org域的电子邮件

const context = $evaluation.getContext();
const identity = context.getIdentity();
const attributes = identity.getAttributes();
const email = attributes.getValue('email').asString(0);

if (identity.hasRealmRole('admin') || email.endsWith('@keycloak.org')) {
    $evaluation.grant();
}
在编写自己的规则时,请记住**$evaluation**对象是一个实现**org.keycloak.authorization.policy.evaluation.Evaluation**的对象。有关您可以从此接口访问的内容的更多信息,请参阅评估 API

基于时间的策略

您可以使用这种类型的策略为您的权限定义时间条件。

要创建新的基于时间的策略,请在策略列表右上角的项目列表中选择**时间**。

添加时间策略

Add Time Policy

配置

  • 名称

    一个描述策略的人类可读且唯一的字符串。最佳实践是使用与您的业务和安全需求密切相关的名称,以便您可以更轻松地识别它们。

  • 描述

    包含有关此策略的详细信息的字符串。

  • 开始时间

    定义在该时间之前必须**不**授予访问权限的时间。只有当当前日期/时间晚于或等于此值时,才授予权限。

  • 过期时间

    定义在该时间之后必须**不**授予访问权限的时间。只有当当前日期/时间早于或等于此值时,才授予权限。选择**重复**以在特定的**月日**、**月**、**年**、**小时**或**分钟**上重复授予访问权限。

  • 月日

    定义必须授予访问权限的月份中的日期。您也可以指定日期范围。在这种情况下,只有当当前月份中的日期在指定两个值之间或等于指定两个值时,才授予权限。

  • 定义必须授予访问权限的月份。您也可以指定月份范围。在这种情况下,只有当当前月份在指定两个值之间或等于指定两个值时,才授予权限。

  • 定义必须授予访问权限的年份。您也可以指定年份范围。在这种情况下,只有当当前年份在指定两个值之间或等于指定两个值时,才授予权限。

  • 小时

    定义必须授予访问权限的小时。您也可以指定小时范围。在这种情况下,只有当当前小时在指定两个值之间或等于指定两个值时,才授予权限。

  • 分钟

    定义必须授予访问权限的分钟。您也可以指定分钟范围。在这种情况下,只有当当前分钟在指定两个值之间或等于指定两个值时,才授予权限。

  • 逻辑

    在其他条件评估完毕后应用的此策略的逻辑。

只有在满足所有条件时才会授予访问权限。Keycloak 将根据每个条件的结果执行AND操作。

其他资源

聚合策略

如前所述,Keycloak 允许您构建策略的策略,这是一个称为策略聚合的概念。您可以使用策略聚合来重用现有策略以构建更复杂的策略,并使您的权限与在处理授权请求期间评估的策略更加分离。

要创建新的聚合策略,请从策略类型列表中选择**聚合**。

添加聚合策略

Add aggregated policy

假设您有一个名为机密资源的资源,只有来自keycloak.org域和特定 IP 地址范围内的用户才能访问。您可以创建包含这两个条件的单个策略。但是,您希望重用此策略的域部分以应用于无论来源网络如何操作的权限。

您可以分别创建用于域和网络条件的策略,并基于这两个策略的组合创建第三个策略。使用聚合策略,您可以自由地组合其他策略,然后将新的聚合策略应用于您想要的任何权限。

在创建聚合策略时,请注意您没有在策略之间引入循环引用或依赖关系。如果检测到循环依赖关系,您将无法创建或更新策略。

配置

  • 名称

    描述策略的人类可读且唯一的字符串。我们强烈建议您使用与您的业务和安全需求密切相关的名称,以便您可以更轻松地识别它们,并了解它们的含义。

  • 描述

    有关此策略的更多详细信息的字符串。

  • 应用策略

    定义一组或多组要与聚合策略关联的策略。要关联策略,您可以选择现有策略,也可以通过选择要创建的策略类型来创建新策略。

  • 决策策略

    此权限的决策策略。

  • 逻辑

    在其他条件评估完毕后应用的此策略的逻辑。

其他资源

聚合策略的决策策略

在创建聚合策略时,您还可以定义将用于根据每个策略的结果确定最终决策的决策策略。

  • 一致

    如果没有提供,则为默认策略。在这种情况下,所有策略都必须评估为正面决策才能使最终决策也为正面。

  • 肯定

    在这种情况下,至少一个策略必须评估为正面决策才能使最终决策也为正面。

  • 共识

    在这种情况下,正面决策的数量必须大于负面决策的数量。如果正面决策和负面决策的数量相同,则最终决策将为负面。

基于客户端的策略

您可以使用这种类型的策略为您的权限定义条件,其中一组或多组客户端被允许访问对象。

要创建新的基于客户端的策略,请从策略类型列表中选择**客户端**。

添加客户端策略

Add a Client Policy

配置

  • 名称

    一个人类可读且唯一的字符串,用于标识策略。最佳实践是使用与您的业务和安全需求密切相关的名称,以便您可以更轻松地识别它们。

  • 描述

    包含有关此策略的详细信息的字符串。

  • 客户端

    指定哪些客户端通过此策略获得了基于组的策略访问权限。

  • 逻辑

    在其他条件评估完毕后应用的此策略的逻辑。

其他资源

基于组的策略

您可以使用这种类型的策略为您的权限定义条件,其中一组或多组组(及其层次结构)被允许访问对象。

要创建新的基于组的策略,请从策略类型列表中选择**组**。

组策略

Add Group Policy

配置

  • 名称

    一个描述策略的人类可读且唯一的字符串。最佳实践是使用与您的业务和安全需求密切相关的名称,以便您可以更轻松地识别它们。

  • 描述

    包含有关此策略的详细信息的字符串。

  • 组声明

    指定令牌中保存组名称和/或路径的声明的名称。通常,授权请求是基于之前颁发给代表某些用户操作的客户端的 ID 令牌或访问令牌进行处理的。如果定义,令牌必须包含一个声明,从该声明中此策略将获取用户是成员的组。如果未定义,用户组将从您的领域配置中获取。

  • 允许您选择在评估权限时应由此策略执行的组。添加组后,您可以通过选中**扩展到子级**复选框来将访问权限扩展到组的子级。如果未选中,访问限制仅适用于所选组。

  • 逻辑

    在其他条件评估完毕后应用的此策略的逻辑。

其他资源

将访问权限扩展到子组

默认情况下,当您将组添加到此策略时,访问限制将仅适用于所选组的成员。

在某些情况下,可能需要不仅允许访问组本身,还允许访问层次结构中的任何子组。对于添加的任何组,您可以选中**扩展到子级**复选框以将访问权限扩展到子组。

将访问权限扩展到子组

Extending access to child groups

在上面的示例中,策略授予任何是**IT**成员的用户或其任何子级的用户访问权限。

基于客户端范围的策略

您可以使用这种类型的策略来定义权限的条件,其中允许一组或多组客户端范围访问对象。

默认情况下,添加到此策略的客户端范围未指定为必需,如果请求访问的客户端已授予这些客户端范围中的任何一个,则策略将授予访问权限。但是,如果您想强制执行特定的客户端范围,可以将特定客户端范围指定为必需

要创建新的基于客户端范围的策略,请从策略类型列表中选择客户端范围

添加客户端范围策略

Add Client Scope Policy

配置

  • 名称

    一个描述策略的人类可读且唯一的字符串。最佳实践是使用与您的业务和安全需求密切相关的名称,以便您可以更轻松地识别它们。

  • 描述

    包含有关此策略的详细信息的字符串。

  • 客户端范围

    指定此策略允许哪些客户端范围。

  • 逻辑

    在其他条件评估完毕后应用的此策略的逻辑。

其他资源

将客户端范围定义为必需

创建基于客户端范围的策略时,可以将特定客户端范围指定为必需。当您这样做时,该策略将仅在请求访问的客户端已授予所有必需的客户端范围时才授予访问权限。

必需客户端范围的示例

Example of required client scope

要将客户端范围指定为必需,请选择要配置为必需的客户端范围的必需复选框。

当您的策略定义多个客户端范围,但其中只有一部分是必需时,必需的客户端范围很有用。

基于正则表达式的策略

您可以使用这种类型的策略来定义权限的正则表达式条件。

要创建新的基于正则表达式的策略,请从策略类型列表中选择正则表达式

此策略解析从当前身份获取的属性。

添加正则表达式策略

Add Regex Policy

配置

  • 名称

    一个描述策略的人类可读且唯一的字符串。最佳实践是使用与您的业务和安全需求密切相关的名称,以便您可以更轻松地识别它们。

  • 描述

    包含有关此策略的详细信息的字符串。

  • 目标声明

    指定令牌中目标声明的名称。对于基于 JSON 的声明,您可以使用点表示法进行嵌套,并使用方括号通过索引访问数组字段。例如,contact.address[0].country。如果目标声明引用 JSON 对象,则第一个路径(例如,contact)应映射到保存 JSON 对象的属性名称。

  • 正则表达式模式

    指定正则表达式模式。

  • 逻辑

    在其他条件评估后要应用的此策略的逻辑

正向和负向逻辑

策略可以使用正向或负向逻辑进行配置。简而言之,您可以使用此选项来定义策略结果应该是保持不变还是取反。

例如,假设您想创建一个策略,其中只有授予特定角色的用户才能获得访问权限。在这种情况下,您可以使用该角色创建一个基于角色的策略,并将它的逻辑字段设置为负向。如果您保留正向(这是默认行为),则策略结果将保持不变。

策略评估 API

使用 JavaScript 编写基于规则的策略时,Keycloak 提供了一个评估 API,该 API 提供有用的信息来帮助确定是否应该授予权限。

此 API 包含一些接口,可以帮助您访问信息,例如

  • 正在评估的权限,代表请求的资源和范围。

  • 与请求的资源相关的属性

  • 运行时环境和与执行上下文相关的任何其他属性

  • 有关用户的信息,例如组成员资格和角色

主要接口是org.keycloak.authorization.policy.evaluation.Evaluation,它定义了以下契约

public interface Evaluation {

    /**
     * Returns the {@link ResourcePermission} to be evaluated.
     *
     * @return the permission to be evaluated
     */
    ResourcePermission getPermission();

    /**
     * Returns the {@link EvaluationContext}. Which provides access to the whole evaluation runtime context.
     *
     * @return the evaluation context
     */
    EvaluationContext getContext();

    /**
     * Returns a {@link Realm} that can be used by policies to query information.
     *
     * @return a {@link Realm} instance
     */
    Realm getRealm();

    /**
     * Grants the requested permission to the caller.
     */
    void grant();

    /**
     * Denies the requested permission.
     */
    void deny();
}

在处理授权请求时,Keycloak 会在评估任何策略之前创建一个Evaluation实例。然后,将此实例传递给每个策略,以确定访问是GRANT还是DENY

策略通过调用Evaluation实例上的grant()deny()方法来确定这一点。默认情况下,Evaluation实例的状态为拒绝,这意味着您的策略必须明确调用grant()方法来指示策略评估引擎应该授予权限。

其他资源

评估上下文

评估上下文在策略评估期间为策略提供有用的信息。

public interface EvaluationContext {

    /**
     * Returns the {@link Identity} that represents an entity (person or non-person) to which the permissions must be granted, or not.
     *
     * @return the identity to which the permissions must be granted, or not
     */
    Identity getIdentity();

    /**
     * Returns all attributes within the current execution and runtime environment.
     *
     * @return the attributes within the current execution and runtime environment
     */
    Attributes getAttributes();
}

通过此接口,策略可以获得

  • 已验证的Identity

  • 有关执行上下文和运行时环境的信息

Identity是根据与授权请求一起发送的 OAuth2 访问令牌构建的,此构造可以访问从原始令牌提取的所有声明。例如,如果您使用协议映射器在 OAuth2 访问令牌中包含自定义声明,您也可以从策略访问此声明,并使用它来构建您的条件。

EvaluationContext还允许您访问与执行和运行时环境相关的属性。目前,只有少数内置属性。

表 1. 执行和运行时属性
名称 描述 类型

kc.time.date_time

当前日期和时间

字符串。格式为MM/dd/yyyy hh:mm:ss

kc.client.network.ip_address

客户端的 IPv4 地址

字符串

kc.client.network.host

客户端的主机名

字符串

kc.client.id

客户端 ID

字符串

kc.client.user_agent

'User-Agent' HTTP 标头的值

字符串[]

kc.realm.name

领域的名称

字符串

管理权限

权限将要保护的对象与必须评估以决定是否应该授予访问权限的策略相关联。

在创建要保护的资源和要用来保护这些资源的策略之后,您可以开始管理权限。要管理权限,请在编辑资源服务器时单击权限选项卡。

权限

Permissions

可以创建权限来保护两种主要类型的对象

  • 资源

  • 范围

要创建权限,请从权限列表右上角的项目列表中选择要创建的权限类型。以下部分将更详细地描述这两种类型的对象。

创建基于资源的权限

基于资源的权限定义了一组或多组资源,使用一组或多组授权策略来保护这些资源。

要创建新的基于资源的权限,请从创建权限下拉菜单中选择创建基于资源的权限

添加资源权限

Add Resource Permission

配置

  • 名称

    描述权限的可读且唯一的字符串。最佳实践是使用与您的业务和安全要求密切相关的名称,以便您可以更轻松地识别它们。

  • 描述

    包含有关此权限的详细信息的字符串。

  • 应用于资源类型

    指定权限是否应用于具有给定类型的全部资源。选择此字段时,系统会提示您输入要保护的资源类型。

    • 资源类型

      定义要保护的资源类型。定义后,将对匹配该类型的全部资源评估此权限。

  • 资源

    定义一组或多组要保护的资源。

  • 策略

    定义一组或多组要与权限关联的策略。要关联策略,您可以选择现有策略,也可以通过选择要创建的策略类型来创建新策略。

  • 决策策略

    此权限的决策策略

类型化资源权限

资源权限还可以用于定义要应用于具有给定类型的所有资源的策略。当您拥有共享共同访问要求和约束的资源时,这种基于资源的权限形式可能很有用。

通常,应用程序中的资源可以根据它们封装的数据或提供的功能进行分类(或类型化)。例如,金融应用程序可以管理不同的银行账户,每个账户都属于特定客户。尽管它们是不同的银行账户,但它们共享银行机构全局定义的通用安全要求和约束。使用类型化资源权限,您可以定义要应用于所有银行账户的通用策略,例如

  • 只有所有者可以管理自己的账户

  • 只允许从所有者的国家/地区或地区访问

  • 强制执行特定的身份验证方法

要创建类型化资源权限,请在创建新的基于资源的权限时单击应用于资源类型。将应用于资源类型设置为开启,您可以指定要保护的类型以及要应用于管理对您指定类型的所有资源的访问的策略。

类型化资源权限示例

Example of a typed resource permission

创建基于范围的权限

基于范围的权限定义了一组一个或多个范围,使用一组一个或多个授权策略来保护这些范围。与基于资源的权限不同,您可以使用此权限类型来创建不仅针对资源,而且还针对与资源关联的范围的权限,从而在定义管理您的资源和可以对它们执行的操作的权限时提供更多粒度。

要创建新的基于范围的权限,请从创建权限下拉菜单中选择创建基于范围的权限

添加范围权限

Add Scope Permission

配置

  • 名称

    描述权限的可读且唯一的字符串。最佳实践是使用与您的业务和安全要求密切相关的名称,以便您可以更轻松地识别它们。

  • 描述

    包含有关此权限的详细信息的字符串。

  • 资源

    将范围限制为与选定资源关联的范围。如果没有选择,则所有范围都可用。

  • 范围

    定义一组一个或多个要保护的范围。

  • 策略

    定义一组或多组要与权限关联的策略。要关联策略,您可以选择现有策略,也可以通过选择要创建的策略类型来创建新策略。

  • 决策策略

    此权限的决策策略

策略决策策略

将策略与权限关联时,您还可以定义决策策略以指定如何评估关联策略的结果以确定访问权限。

  • 一致

    如果没有提供,则为默认策略。在这种情况下,所有策略都必须评估为正面决策才能使最终决策也为正面。

  • 肯定

    在这种情况下,至少一项策略必须评估为正面决策才能使最终决策也为正面。

  • 共识

    在这种情况下,正面决策的数量必须大于负面决策的数量。如果正面决策和负面决策的数量相等,则最终决策将为负面。

评估和测试策略

在设计策略时,您可以模拟授权请求以测试策略的评估方式。

您可以在编辑资源服务器时单击评估选项卡访问策略评估工具。在那里,您可以指定不同的输入来模拟实际的授权请求并测试策略的效果。

策略评估工具

Policy evaluation tool

提供身份信息

身份信息过滤器可用于指定请求权限的用户。

提供上下文信息

上下文信息过滤器可用于为评估上下文定义附加属性,以便策略可以获取这些相同的属性。

提供权限

权限过滤器可用于构建授权请求。您可以为一组一个或多个资源和范围请求权限。如果您想根据所有受保护的资源和范围来模拟授权请求,请单击添加,而无需指定任何资源范围

指定所需的值后,单击评估

授权服务

Keycloak 授权服务建立在 OAuth2 和用户管理访问规范等知名标准之上。

OAuth2 客户端(如前端应用程序)可以通过令牌端点从服务器获取访问令牌,并使用这些相同的令牌访问由资源服务器(如后端服务)保护的资源。同样,Keycloak 授权服务为 OAuth2 提供扩展,以允许根据与请求的资源或范围关联的所有策略的处理来颁发访问令牌。这意味着资源服务器可以根据服务器授予的权限以及访问令牌持有的权限来强制执行对其受保护资源的访问。在 Keycloak 授权服务中,具有权限的访问令牌称为请求方令牌或简称 RPT。

除了颁发 RPT 之外,Keycloak 授权服务还提供了一组 RESTful 端点,允许资源服务器管理其受保护的资源、范围、权限和策略,帮助开发人员将这些功能扩展或集成到他们的应用程序中以支持细粒度授权。

发现授权服务端点和元数据

Keycloak 提供了一个发现文档,客户端可以从中获取与 Keycloak 授权服务交互所需的所有信息,包括端点位置和功能。

可以从以下位置获取发现文档

curl -X GET \
  http://${host}:${port}/realms/${realm}/.well-known/uma2-configuration

其中${host}:${port}是 Keycloak 运行的主机名(或 IP 地址)和端口,${realm}是 Keycloak 中一个域的名称。

结果,您应该得到以下响应

{

    // some claims are expected here

    // these are the main claims in the discovery document about Authorization Services endpoints location
    "token_endpoint": "http://${host}:${port}/realms/${realm}/protocol/openid-connect/token",
    "token_introspection_endpoint": "http://${host}:${port}/realms/${realm}/protocol/openid-connect/token/introspect",
    "resource_registration_endpoint": "http://${host}:${port}/realms/${realm}/authz/protection/resource_set",
    "permission_endpoint": "http://${host}:${port}/realms/${realm}/authz/protection/permission",
    "policy_endpoint": "http://${host}:${port}/realms/${realm}/authz/protection/uma-policy"
}

每个端点都公开一组特定的功能

  • token_endpoint

    一个符合 OAuth2 标准的令牌端点,它支持urn:ietf:params:oauth:grant-type:uma-ticket授权类型。通过此端点,客户端可以发送授权请求并获得包含 Keycloak 授予的所有权限的 RPT。

  • token_introspection_endpoint

    一个符合 OAuth2 标准的令牌内省端点,客户端可以使用它查询服务器以确定 RPT 的活动状态,并确定与令牌关联的任何其他信息,例如 Keycloak 授予的权限。

  • resource_registration_endpoint

    一个符合 UMA 标准的资源注册端点,资源服务器可以使用它来管理其受保护的资源和范围。此端点提供在 Keycloak 中创建、读取、更新和删除资源和范围的操作。

  • permission_endpoint

    一个符合 UMA 标准的权限端点,资源服务器可以使用它来管理权限票证。此端点提供在 Keycloak 中创建、读取、更新和删除权限票证的操作。

获取权限

要从 Keycloak 获取权限,请将授权请求发送到令牌端点。结果,Keycloak 将评估与所请求的资源和范围关联的所有策略,并颁发包含服务器授予的所有权限的 RPT。

客户端可以使用以下参数将授权请求发送到令牌端点

  • grant_type

    此参数是必需的。必须为urn:ietf:params:oauth:grant-type:uma-ticket

  • ticket

    此参数是可选的。客户端在 UMA 授权过程中收到的最新权限票证。

  • claim_token

    此参数是可选的。一个字符串,表示在评估对所请求的资源和范围的权限时服务器应考虑的额外声明。此参数允许客户端将声明推送到 Keycloak。有关所有支持的令牌格式的更多详细信息,请参阅claim_token_format参数。

  • claim_token_format

    此参数是可选的。一个字符串,指示claim_token参数中指定的令牌的格式。Keycloak 支持两种令牌格式:urn:ietf:params:oauth:token-type:jwthttps://openid.net/specs/openid-connect-core-1_0.html#IDTokenurn:ietf:params:oauth:token-type:jwt格式表示claim_token参数引用一个访问令牌。https://openid.net/specs/openid-connect-core-1_0.html#IDToken表示claim_token参数引用一个 OpenID Connect ID 令牌。

  • rpt

    此参数是可选的。先前颁发的 RPT,其权限也应该被评估并添加到新的 RPT 中。此参数允许拥有 RPT 的客户端执行增量授权,其中权限按需添加。

  • permission

    此参数是可选的。一个字符串,表示客户端正在寻求访问权限的一组一个或多个资源和范围。为了请求对多个资源和范围的权限,此参数可以多次定义。此参数是对urn:ietf:params:oauth:grant-type:uma-ticket授权类型的扩展,以允许客户端在没有权限票证的情况下发送授权请求。字符串的格式必须为:RESOURCE_ID#SCOPE_ID。例如:Resource A#Scope AResource A#Scope A, Scope B, Scope CResource A#Scope A

  • permission_resource_format

    此参数为可选。一个字符串,表示permission参数中资源的格式。可能的值为iduriid表示格式为RESOURCE_IDuri表示格式为URI。如果未指定,则默认值为id

  • permission_resource_matching_uri

    此参数为可选。一个布尔值,表示是否在permission参数中使用路径匹配来表示 URI 格式的资源。如果未指定,则默认值为 false。

  • audience

    此参数为可选。客户端正在请求访问的资源服务器的客户端标识符。如果定义了permission参数,则此参数为必填项。它作为 Keycloak 的提示,指示应在哪个上下文中评估权限。

  • response_include_resource_name

    此参数为可选。一个布尔值,指示服务器是否应在 RPT 的权限中包含资源名称。如果为 false,则仅包含资源标识符。

  • response_permissions_limit

    此参数为可选。一个整数 N,定义 RPT 可以具有的权限数量的限制。当与rpt参数一起使用时,仅 RPT 中保留最后 N 个请求的权限。

  • submit_request

    此参数为可选。一个布尔值,指示服务器是否应创建对权限票证引用的资源和范围的权限请求。此参数仅在与ticket参数一起使用作为 UMA 授权过程的一部分时有效。

  • response_mode

    此参数为可选。一个字符串值,指示服务器应如何响应授权请求。此参数在您主要对服务器的整体决策或授予的权限感兴趣,而不是标准的 OAuth2 响应时特别有用。可能的值为

    • decision

      指示来自服务器的响应应仅表示整体决策,通过返回以下格式的 JSON

      {
          'result': true
      }

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

    • permissions

      指示来自服务器的响应应包含服务器授予的任何权限,通过返回以下格式的 JSON

      [
          {
              'rsid': 'My Resource'
              'scopes': ['view', 'update']
          },
      
          ...
      ]

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

当客户端正在寻求访问由资源服务器保护的两个资源时的授权请求示例。

curl -X POST \
  http://${host}:${port}/realms/${realm}/protocol/openid-connect/token \
  -H "Authorization: Bearer ${access_token}" \
  --data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" \
  --data "audience={resource_server_client_id}" \
  --data "permission=Resource A#Scope A" \
  --data "permission=Resource B#Scope B"

当客户端正在寻求访问由资源服务器保护的任何资源和范围时的授权请求示例。注意:这不会评估所有资源的权限。相反,将评估由资源服务器拥有的资源、请求用户拥有的资源以及其他所有者明确授予请求用户的资源的权限。

curl -X POST \
  http://${host}:${port}/realms/${realm}/protocol/openid-connect/token \
  -H "Authorization: Bearer ${access_token}" \
  --data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" \
  --data "audience={resource_server_client_id}"

当客户端在从资源服务器接收权限票证作为授权过程的一部分后,正在寻求访问 UMA 保护的资源时的授权请求示例

curl -X POST \
  http://${host}:${port}/realms/${realm}/protocol/openid-connect/token \
  -H "Authorization: Bearer ${access_token}" \
  --data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" \
  --data "ticket=${permission_ticket}

如果 Keycloak 评估过程导致签发权限,它将签发已将权限关联到的 RPT

Keycloak 使用 RPT 响应客户端
HTTP/1.1 200 OK
Content-Type: application/json
...
{
    "access_token": "${rpt}",
}

来自服务器的响应就像使用其他授权类型时来自令牌端点的任何其他响应一样。RPT 可以从access_token响应参数中获取。如果客户端未经授权,Keycloak 将使用403 HTTP 状态代码进行响应

Keycloak 拒绝授权请求
HTTP/1.1 403 Forbidden
Content-Type: application/json
...
{
    "error": "access_denied",
    "error_description": "request_denied"
}

客户端身份验证方法

客户端需要向令牌端点进行身份验证,以获取 RPT。当使用urn:ietf:params:oauth:grant-type:uma-ticket授权类型时,客户端可以使用以下任何身份验证方法

  • 承载令牌

    客户端应将访问令牌作为承载凭证发送到 HTTP 授权标头,以访问令牌端点。

    示例:使用访问令牌进行身份验证以访问令牌端点的授权请求
    curl -X POST \
      http://${host}:${port}/realms/${realm}/protocol/openid-connect/token \
      -H "Authorization: Bearer ${access_token}" \
      --data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket"

    此方法在客户端代表用户执行操作时特别有用。在这种情况下,承载令牌是由 Keycloak 之前签发给代表用户(或代表自身)执行操作的某个客户端的访问令牌。将根据访问令牌所代表的访问上下文评估权限。例如,如果访问令牌是签发给代表用户 A 执行操作的客户端 A,则将根据用户 A 拥有的资源和范围授予权限。

  • 客户端凭据

    客户端可以使用 Keycloak 支持的任何客户端身份验证方法。例如,client_id/client_secret 或 JWT。

    示例:使用客户端 ID 和客户端密钥进行身份验证以访问令牌端点的授权请求
    curl -X POST \
      http://${host}:${port}/realms/${realm}/protocol/openid-connect/token \
      -H "Authorization: Basic cGhvdGg6L7Jl13RmfWgtkk==pOnNlY3JldA==" \
      --data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket"

推送声明

从服务器获取权限时,您可以推送任意声明,以便在评估权限时将这些声明提供给您的策略。

如果您是从服务器使用权限票证(UMA 流程)获取权限,则可以按如下方式向令牌端点发送授权请求

curl -X POST \
  http://${host}:${port}/realms/${realm}/protocol/openid-connect/token \
  --data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" \
  --data "claim_token=ewogICAib3JnYW5pemF0aW9uIjogWyJhY21lIl0KfQ==" \
  --data "claim_token_format=urn:ietf:params:oauth:token-type:jwt" \
  --data "client_id={resource_server_client_id}" \
  --data "client_secret={resource_server_client_secret}" \
  --data "audience={resource_server_client_id}"

claim_token参数期望一个 BASE64 编码的 JSON,其格式类似于以下示例

{
    "organization" : ["acme"]
}

此格式期望一个或多个声明,其中每个声明的值必须是一个字符串数组。

使用 UMA 推送声明

有关在使用 UMA 和权限票证时如何推送声明的更多详细信息,请查看权限 API

用户管理的访问

Keycloak 授权服务基于用户管理的访问,简称 UMA。UMA 是一种规范,它通过以下方式增强了 OAuth2 功能

  • 隐私

    如今,随着越来越多的数据和设备可用并连接到云,用户隐私正成为人们越来越关注的问题。使用 UMA 和 Keycloak,资源服务器可以增强其功能,以改进其对用户隐私的资源保护方式,其中权限是根据用户定义的策略授予的。

  • 点对点授权

    资源所有者(例如:普通最终用户)可以管理对其资源的访问并授权其他方(例如:普通最终用户)访问这些资源。这与 OAuth2 不同,在 OAuth2 中,同意是授予代表用户执行操作的客户端应用程序的,在 UMA 中,资源所有者被允许以完全异步的方式同意对其他用户的访问。

  • 资源共享

    资源所有者被允许管理对其资源的权限,并决定谁可以访问特定资源以及如何访问。然后,Keycloak 可以充当共享管理服务,从该服务中,资源所有者可以管理其资源。

Keycloak 是一款符合 UMA 2.0 标准的授权服务器,它提供大多数 UMA 功能。

例如,假设用户 Alice(资源所有者)使用互联网银行服务(资源服务器)来管理她的银行账户(资源)。有一天,Alice 决定将她的银行账户开放给 Bob(请求方),一位会计专业人士。但是,Bob 只能查看(范围)Alice 的账户。

作为资源服务器,互联网银行服务必须能够保护 Alice 的银行账户。为此,它依赖 Keycloak 资源注册端点在服务器中创建一个表示 Alice 银行账户的资源。

此时,如果 Bob 尝试访问 Alice 的银行账户,将被拒绝访问。互联网银行服务为银行账户定义了一些默认策略。其中之一是只有所有者,在本例中是 Alice,被允许访问她的银行账户。

但是,出于对 Alice 隐私的尊重,互联网银行服务还允许她更改银行账户的特定策略。她可以更改的其中一项策略是定义哪些人被允许查看她的银行账户。为此,互联网银行服务依赖 Keycloak 为 Alice 提供一个空间,她可以在其中选择个人以及他们被允许访问的操作(或数据)。Alice 可以随时撤销访问权限或授予 Bob 更多权限。

授权过程

在 UMA 中,授权过程从客户端尝试访问 UMA 保护的资源服务器时开始。

UMA 保护的资源服务器在请求中期望承载令牌,其中令牌是 RPT。当客户端在没有 RPT 的情况下向资源服务器请求资源时

客户端请求受保护的资源,但未发送 RPT
curl -X GET \
  http://${host}:${port}/my-resource-server/resource/1bfdfe78-a4e1-4c2d-b142-fc92b75b986f

资源服务器向客户端发送带有权限ticketas_uri参数的响应,其中包含应将票证发送到的 Keycloak 服务器的位置,以获取 RPT。

资源服务器响应带有权限票证
HTTP/1.1 401 Unauthorized
WWW-Authenticate: UMA realm="${realm}",
    as_uri="https://${host}:${port}/realms/${realm}",
    ticket="016f84e8-f9b9-11e0-bd6f-0021cc6004de"

权限票证是由 Keycloak 权限 API 签发的特殊类型的令牌。它们代表正在请求的权限(例如:资源和范围),以及与请求相关的任何其他信息。只有资源服务器被允许创建这些令牌。

现在,客户端已经拥有权限票证以及 Keycloak 服务器的位置,客户端可以使用发现文档获取令牌端点的位置,并发送授权请求。

客户端向令牌端点发送授权请求以获取 RPT
curl -X POST \
  http://${host}:${port}/realms/${realm}/protocol/openid-connect/token \
  -H "Authorization: Bearer ${access_token}" \
  --data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" \
  --data "ticket=${permission_ticket}

如果 Keycloak 评估过程导致签发权限,它将签发已将权限关联到的 RPT

Keycloak 使用 RPT 响应客户端
HTTP/1.1 200 OK
Content-Type: application/json
...
{
    "access_token": "${rpt}",
}

来自服务器的响应就像使用其他授权类型时来自令牌端点的任何其他响应一样。RPT 可以从access_token响应参数中获取。如果客户端没有获得权限的授权,Keycloak 将使用403 HTTP 状态代码进行响应

Keycloak 拒绝授权请求
HTTP/1.1 403 Forbidden
Content-Type: application/json
...
{
    "error": "access_denied",
    "error_description": "request_denied"
}

提交权限请求

作为授权过程的一部分,客户端需要首先从 UMA 保护的资源服务器获取权限票据,以便在 Keycloak 令牌端点将其与 RPT 进行交换。

默认情况下,如果客户端无法获得 RPT,Keycloak 会返回 403 HTTP 状态码和 request_denied 错误。

Keycloak 拒绝授权请求
HTTP/1.1 403 Forbidden
Content-Type: application/json
...
{
    "error": "access_denied",
    "error_description": "request_denied"
}

此响应意味着 Keycloak 无法使用权限票据中表示的权限颁发 RPT。

在某些情况下,客户端应用程序可能希望启动异步授权流程,并让被请求资源的所有者决定是否授予访问权限。为此,客户端可以在授权请求中使用 submit_request 请求参数,并将其发送到令牌端点。

curl -X POST \
  http://${host}:${port}/realms/${realm}/protocol/openid-connect/token \
  -H "Authorization: Bearer ${access_token}" \
  --data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" \
  --data "ticket=${permission_ticket} \
  --data "submit_request=true"

当使用 submit_request 参数时,Keycloak 会为每个被拒绝访问的资源持久化一个权限请求。创建后,资源所有者可以检查其帐户并管理其权限请求。

您可以将此功能视为应用程序中的“请求访问”按钮,用户可以使用此按钮向其他用户请求对其资源的访问权限。

管理对用户资源的访问权限

用户可以使用 Keycloak 帐户控制台管理对其资源的访问权限。要启用此功能,您必须首先为您的 realm 启用用户管理的访问权限。

步骤
  1. 登录管理控制台。

  2. 在菜单中单击“Realm 设置”。

  3. 将“用户管理的访问权限”切换到“开启”。

  4. 单击管理控制台右上角的用户名,然后选择“管理帐户”。

    My Resources

  5. 在菜单选项中单击“我的资源”。将显示一个包含以下选项的页面。

    • 管理“我的资源”

      本节包含用户拥有的所有资源的列表。用户可以单击资源以获取更多详细信息,并与他人共享资源。当有等待批准的权限请求时,将在资源名称旁边显示一个图标。这些请求与请求访问特定资源的各方(用户)相关联。用户可以批准或拒绝这些请求。您可以通过单击图标来执行此操作。

      Resource Detail

    • 管理“与我共享的资源”

      本节包含与用户共享的所有资源的列表。

    • 管理“有权访问此资源的人员”

      本节包含有权访问此资源的人员的列表。用户可以通过单击“撤销”按钮或删除特定“权限”来撤销访问权限。

    • 与他人共享资源

      通过键入其他用户的用户名或电子邮件,用户可以共享资源并选择要授予的权限。

保护 API

保护 API 提供了一组与 UMA 兼容的端点,这些端点提供以下功能:

  • 资源管理

    使用此端点,资源服务器可以远程管理其资源,并使 策略执行器 能够查询服务器以获取需要保护的资源。

  • 权限管理

    在 UMA 协议中,资源服务器访问此端点以创建权限票据。Keycloak 还提供了端点来管理权限状态和查询权限。

  • 策略 API

    Keycloak 利用 UMA 保护 API 来允许资源服务器管理其用户的权限。除了资源和权限 API 之外,Keycloak 还提供了一个策略 API,资源服务器可以代表其用户从该 API 中设置资源的权限。

此 API 的一项重要要求是,**只有** 资源服务器被允许使用称为保护 API 令牌 (PAT) 的特殊 OAuth2 访问令牌访问其端点。在 UMA 中,PAT 是一种具有范围 **uma_protection** 的令牌。

什么是 PAT 以及如何获取 PAT

**保护 API 令牌** (PAT) 是一种特殊的 OAuth2 访问令牌,其范围定义为 **uma_protection**。创建资源服务器时,Keycloak 会自动为相应的客户端应用程序创建一个角色 uma_protection,并将其与客户端的服务帐户关联。

授予 **uma_protection** 角色的服务帐户

Service Account granted with uma_protection role

资源服务器可以像获取任何其他 OAuth2 访问令牌一样从 Keycloak 获取 PAT。例如,使用 curl

curl -X POST \
    -H "Content-Type: application/x-www-form-urlencoded" \
    -d 'grant_type=client_credentials&client_id=${client_id}&client_secret=${client_secret}' \
    "http://${host}:${port}/realms/${realm_name}/protocol/openid-connect/token"

上面的示例使用 **client_credentials** 授予类型从服务器获取 PAT。结果,服务器返回类似于以下内容的响应:

{
  "access_token": ${PAT},
  "expires_in": 300,
  "refresh_expires_in": 1800,
  "refresh_token": ${refresh_token},
  "token_type": "bearer",
  "id_token": ${id_token},
  "not-before-policy": 0,
  "session_state": "ccea4a55-9aec-4024-b11c-44f6f168439e"
}
Keycloak 可以通过不同的方式对您的客户端应用程序进行身份验证。为简单起见,此处使用 **client_credentials** 授予类型,它需要 client_idclient_secret。您可以选择使用任何受支持的身份验证方法。

管理资源

资源服务器可以使用与 UMA 兼容的端点远程管理其资源。

http://${host}:${port}/realms/${realm_name}/authz/protection/resource_set

此端点提供以下概述的操作(为清晰起见,省略了整个路径)

  • 创建资源集描述:POST /resource_set

  • 读取资源集描述:GET /resource_set/{_id}

  • 更新资源集描述:PUT /resource_set/{_id}

  • 删除资源集描述:DELETE /resource_set/{_id}

  • 列出资源集描述:GET /resource_set

有关每个操作的契约的更多信息,请参阅 UMA 资源注册 API

创建资源

要创建资源,您必须发送以下 HTTP POST 请求:

curl -v -X POST \
  http://${host}:${port}/realms/${realm_name}/authz/protection/resource_set \
  -H 'Authorization: Bearer '$pat \
  -H 'Content-Type: application/json' \
  -d '{
     "name":"Tweedl Social Service",
     "type":"http://www.example.com/rsrcs/socialstream/140-compatible",
     "icon_uri":"http://www.example.com/icons/sharesocial.png",
     "resource_scopes":[
         "read-public",
         "post-updates",
         "read-private",
         "http://www.example.com/scopes/all"
      ]
  }'

默认情况下,资源的所有者是资源服务器。如果要定义不同的所有者,例如特定用户,则可以发送以下请求:

curl -v -X POST \
  http://${host}:${port}/realms/${realm_name}/authz/protection/resource_set \
  -H 'Authorization: Bearer '$pat \
  -H 'Content-Type: application/json' \
  -d '{
     "name":"Alice Resource",
     "owner": "alice"
  }'

其中属性 owner 可以设置为用户名或用户的标识符。

创建用户管理的资源

默认情况下,通过保护 API 创建的资源无法由资源所有者通过 帐户控制台 管理。

要创建资源并允许资源所有者管理这些资源,您必须设置 ownerManagedAccess 属性,如下所示:

curl -v -X POST \
  http://${host}:${port}/realms/${realm_name}/authz/protection/resource_set \
  -H 'Authorization: Bearer '$pat \
  -H 'Content-Type: application/json' \
  -d '{
     "name":"Alice Resource",
     "owner": "alice",
     "ownerManagedAccess": true
  }'
更新资源

要更新现有资源,请发送以下 HTTP PUT 请求:

curl -v -X PUT \
  http://${host}:${port}/realms/${realm_name}/authz/protection/resource_set/{resource_id} \
  -H 'Authorization: Bearer '$pat \
  -H 'Content-Type: application/json' \
  -d '{
     "_id": "Alice Resource",
     "name":"Alice Resource",
     "resource_scopes": [
        "read"
     ]
  }'
删除资源

要删除现有资源,请发送以下 HTTP DELETE 请求:

curl -v -X DELETE \
  http://${host}:${port}/realms/${realm_name}/authz/protection/resource_set/{resource_id} \
  -H 'Authorization: Bearer '$pat
查询资源

要按 id 查询资源,请发送以下 HTTP GET 请求:

http://${host}:${port}/realms/${realm_name}/authz/protection/resource_set/{resource_id}

要按 name 查询资源,请发送以下 HTTP GET 请求:

http://${host}:${port}/realms/${realm_name}/authz/protection/resource_set?name=Alice Resource

默认情况下,name 过滤器将匹配任何具有给定模式的资源。要将查询限制为仅返回完全匹配的资源,请使用以下命令:

http://${host}:${port}/realms/${realm_name}/authz/protection/resource_set?name=Alice Resource&exactName=true

要按 uri 查询资源,请发送以下 HTTP GET 请求:

http://${host}:${port}/realms/${realm_name}/authz/protection/resource_set?uri=/api/alice

要按 owner 查询资源,请发送以下 HTTP GET 请求:

http://${host}:${port}/realms/${realm_name}/authz/protection/resource_set?owner=alice

要按 type 查询资源,请发送以下 HTTP GET 请求:

http://${host}:${port}/realms/${realm_name}/authz/protection/resource_set?type=albums

要按 scope 查询资源,请发送以下 HTTP GET 请求:

http://${host}:${port}/realms/${realm_name}/authz/protection/resource_set?scope=read

当查询服务器以获取权限时,使用参数 firstmax 结果来限制结果。

管理权限请求

使用 UMA 协议的资源服务器可以使用特定端点来管理权限请求。此端点提供了一个与 UMA 兼容的流程,用于注册权限请求并获取权限票据。

http://${host}:${port}/realms/${realm_name}/authz/protection/permission

权限票据 是一种特殊的安全令牌类型,表示权限请求。根据 UMA 规范,权限票据是

一种关联句柄,从授权服务器传达给资源服务器,从资源服务器传达给客户端,最终从客户端传达回授权服务器,以使授权服务器能够评估应用于授权数据请求的正确策略。

在大多数情况下,您无需直接处理此端点。Keycloak 提供了一个 策略执行器,它使您的资源服务器能够启用 UMA,以便它可以从授权服务器获取权限票据,将此票据返回给客户端应用程序,并根据最终的请求方令牌 (RPT) 强制执行授权决策。

从 Keycloak 获取权限票证的过程由资源服务器执行,而不是常规的客户端应用程序。当客户端尝试访问受保护的资源,但没有必要的权限访问该资源时,就会获得权限票证。权限票证的签发是使用 UMA 时的一个重要方面,因为它允许资源服务器

  • 从客户端抽象出与资源服务器保护的资源相关联的数据

  • 在 Keycloak 授权请求中注册,这些请求可以在工作流中用于根据资源所有者的同意授予访问权限

  • 将资源服务器与授权服务器解耦,并允许它们使用不同的授权服务器来保护和管理其资源

从客户端的角度来看,权限票证也有一些值得强调的重要方面

  • 客户端不需要了解授权数据是如何与受保护的资源相关联的。权限票证对客户端完全透明。

  • 客户端可以访问不同资源服务器上的资源,这些资源受到不同授权服务器的保护

这些只是 UMA 带来的好处的一部分,其中 UMA 的其他方面也高度依赖于权限票证,尤其是在隐私和用户控制其资源访问方面。

创建权限票证

要创建权限票证,请发送以下 HTTP POST 请求

curl -X POST \
  http://${host}:${port}/realms/${realm_name}/authz/protection/permission \
  -H 'Authorization: Bearer '$pat \
  -H 'Content-Type: application/json' \
  -d '[
  {
    "resource_id": "{resource_id}",
    "resource_scopes": [
      "view"
    ]
  }
]'

创建票证时,您还可以推送任意声明并将这些声明与票证关联起来

curl -X POST \
  http://${host}:${port}/realms/${realm_name}/authz/protection/permission \
  -H 'Authorization: Bearer '$pat \
  -H 'Content-Type: application/json' \
  -d '[
  {
    "resource_id": "{resource_id}",
    "resource_scopes": [
      "view"
    ],
    "claims": {
        "organization": ["acme"]
    }
  }
]'

当评估与权限票证关联的资源和范围的权限时,这些声明将可供您的策略使用。

其他非 UMA 兼容端点
创建权限票证

要将对特定资源(id 为 {resource_id})的权限授予 id 为 {user_id} 的用户(作为该资源的所有者),请发送以下 HTTP POST 请求

curl -X POST \
     http://${host}:${port}/realms/${realm_name}/authz/protection/permission/ticket \
     -H 'Authorization: Bearer '$access_token \
     -H 'Content-Type: application/json' \
     -d '{
       "resource": "{resource_id}",
       "requester": "{user_id}",
       "granted": true,
       "scopeName": "view"
     }'
获取权限票证
curl http://${host}:${port}/realms/${realm_name}/authz/protection/permission/ticket \
     -H 'Authorization: Bearer '$access_token

您可以使用以下任何查询参数

  • scopeId

  • resourceId

  • owner

  • requester

  • granted

  • returnNames

  • first

  • max

更新权限票证
curl -X PUT \
     http://${host}:${port}/realms/${realm_name}/authz/protection/permission/ticket \
     -H 'Authorization: Bearer '$access_token \
     -H 'Content-Type: application/json' \
     -d '{
       "id": "{ticket_id}"
       "resource": "{resource_id}",
       "requester": "{user_id}",
       "granted": false,
       "scopeName": "view"
     }'
删除权限票证
curl -X DELETE http://${host}:${port}/realms/${realm_name}/authz/protection/permission/ticket/{ticket_id} \
     -H 'Authorization: Bearer '$access_token

使用策略 API 管理资源权限

Keycloak 利用 UMA 保护 API 来允许资源服务器管理其用户的权限。除了资源和权限 API 之外,Keycloak 还提供了一个策略 API,资源服务器可以代表其用户从该 API 中设置资源的权限。

策略 API 可在以下位置使用

http://${host}:${port}/realms/${realm_name}/authz/protection/uma-policy/{resource_id}

此 API 受承载令牌保护,该令牌必须代表用户授予资源服务器代表其管理权限的同意。承载令牌可以是使用以下方法从令牌端点获得的常规访问令牌

  • 资源所有者密码凭据授予类型

  • 令牌交换,以便将授予某个客户端(公共客户端)的访问令牌交换为目标受众是资源服务器的令牌

将权限与资源关联

要将权限与特定资源关联,您必须发送以下 HTTP POST 请求

curl -X POST \
  http://127.0.0.1:8180/realms/photoz/authz/protection/uma-policy/{resource_id} \
  -H 'Authorization: Bearer '$access_token \
  -H 'Cache-Control: no-cache' \
  -H 'Content-Type: application/json' \
  -d '{
        "name": "Any people manager",
        "description": "Allow access to any people manager",
        "scopes": ["read"],
        "roles": ["people-manager"]
}'

在上面的示例中,我们正在创建并关联一个新的权限,该权限与由 resource_id 表示的资源相关联,其中任何具有 people-manager 角色的用户都应该被授予 read 范围。

您还可以使用其他访问控制机制来创建策略,例如使用组

curl -X POST \
  http://127.0.0.1:8180/realms/photoz/authz/protection/uma-policy/{resource_id} \
  -H 'Authorization: Bearer '$access_token \
  -H 'Cache-Control: no-cache' \
  -H 'Content-Type: application/json' \
  -d '{
        "name": "Any people manager",
        "description": "Allow access to any people manager",
        "scopes": ["read"],
        "groups": ["/Managers/People Managers"]
}'

或特定客户端

curl -X POST \
  http://127.0.0.1:8180/realms/photoz/authz/protection/uma-policy/{resource_id} \
  -H 'Authorization: Bearer '$access_token \
  -H 'Cache-Control: no-cache' \
  -H 'Content-Type: application/json' \
  -d '{
        "name": "Any people manager",
        "description": "Allow access to any people manager",
        "scopes": ["read"],
        "clients": ["my-client"]
}'

甚至使用使用 JavaScript 的自定义策略

curl -X POST \
  http://127.0.0.1:8180/realms/photoz/authz/protection/uma-policy/{resource_id} \
  -H 'Authorization: Bearer '$access_token \
  -H 'Cache-Control: no-cache' \
  -H 'Content-Type: application/json' \
  -d '{
        "name": "Any people manager",
        "description": "Allow access to any people manager",
        "scopes": ["read"],
        "condition": "my-deployed-script.js"
}'

也可以设置这些访问控制机制的任何组合。

要更新现有权限,请发送以下 HTTP PUT 请求

curl -X PUT \
  http://127.0.0.1:8180/realms/photoz/authz/protection/uma-policy/{permission_id} \
  -H 'Authorization: Bearer '$access_token \
  -H 'Content-Type: application/json' \
  -d '{
    "id": "21eb3fed-02d7-4b5a-9102-29f3f09b6de2",
    "name": "Any people manager",
    "description": "Allow access to any people manager",
    "type": "uma",
    "scopes": [
        "album:view"
    ],
    "logic": "POSITIVE",
    "decisionStrategy": "UNANIMOUS",
    "owner": "7e22131a-aa57-4f5f-b1db-6e82babcd322",
    "roles": [
        "user"
    ]
}'
删除权限

要删除与资源关联的权限,请发送以下 HTTP DELETE 请求

curl -X DELETE \
  http://127.0.0.1:8180/realms/photoz/authz/protection/uma-policy/{permission_id} \
  -H 'Authorization: Bearer '$access_token
查询权限

要查询与资源关联的权限,请发送以下 HTTP GET 请求

http://${host}:${port}/realms/${realm}/authz/protection/uma-policy?resource={resource_id}

要查询权限(根据其名称),请发送以下 HTTP GET 请求

http://${host}:${port}/realms/${realm}/authz/protection/uma-policy?name=Any people manager

要查询与特定范围关联的权限,请发送以下 HTTP GET 请求

http://${host}:${port}/realms/${realm}/authz/protection/uma-policy?scope=read

要查询所有权限,请发送以下 HTTP GET 请求

http://${host}:${port}/realms/${realm}/authz/protection/uma-policy

当查询服务器以获取权限时,使用参数 firstmax 结果来限制结果。

请求方令牌

请求方令牌 (RPT) 是一个 JSON Web 令牌 (JWT),使用 JSON Web 签名 (JWS) 进行数字签名。该令牌是基于 Keycloak 之前向代表用户或以自身名义操作的特定客户端发行的 OAuth2 访问令牌构建的。

当您解码 RPT 时,您会看到类似于以下内容的有效负载

{
  "authorization": {
      "permissions": [
        {
          "resource_set_id": "d2fe9843-6462-4bfc-baba-b5787bb6e0e7",
          "resource_set_name": "Hello World Resource"
        }
      ]
  },
  "jti": "d6109a09-78fd-4998-bf89-95730dfd0892-1464906679405",
  "exp": 1464906971,
  "nbf": 0,
  "iat": 1464906671,
  "sub": "f1888f4d-5172-4359-be0c-af338505d86c",
  "typ": "kc_ett",
  "azp": "hello-world-authz-service"
}

从该令牌中,您可以从 permissions 声明中获得服务器授予的所有权限。

还要注意,权限与您正在保护的资源/范围直接相关,并且与用于实际授予和颁发这些相同权限的访问控制方法完全解耦。

内省请求方令牌

有时您可能希望内省请求方令牌 (RPT) 以检查其有效性或获取令牌中的权限以在资源服务器端强制执行授权决策。

令牌内省可以帮助您解决两个主要用例

  • 当客户端应用程序需要查询令牌有效性以获取具有相同或额外权限的新令牌时

  • 当在资源服务器端强制执行授权决策时,尤其是在没有内置 策略执行器 符合您的应用程序时

获取有关 RPT 的信息

令牌内省本质上是一个 OAuth2 令牌内省 兼容端点,您可以从中获取有关 RPT 的信息。

http://${host}:${port}/realms/${realm_name}/protocol/openid-connect/token/introspect

要使用此端点内省 RPT,您可以向服务器发送以下请求

curl -X POST \
    -H "Authorization: Basic aGVsbG8td29ybGQtYXV0aHotc2VydmljZTpzZWNyZXQ=" \
    -H "Content-Type: application/x-www-form-urlencoded" \
    -d 'token_type_hint=requesting_party_token&token=${RPT}' \
    "http://127.0.0.1:8080/realms/hello-world-authz/protocol/openid-connect/token/introspect"
上面的请求使用 HTTP BASIC 并传递客户端凭据(客户端 ID 和密钥)来验证尝试内省令牌的客户端,但您可以使用 Keycloak 支持的任何其他客户端身份验证方法。

内省端点需要两个参数

  • token_type_hint

    requesting_party_token 作为此参数的值,这表示您要内省 RPT。

  • token

    将令牌字符串(如授权过程中服务器返回的)作为此参数的值。

结果,服务器响应为

{
  "permissions": [
    {
      "resource_id": "90ccc6fc-b296-4cd1-881e-089e1ee15957",
      "resource_name": "Hello World Resource"
    }
  ],
  "exp": 1465314139,
  "nbf": 0,
  "iat": 1465313839,
  "aud": "hello-world-authz-service",
  "active": true
}

如果 RPT 不活动,则会返回此响应

{
  "active": false
}

我是否需要每次都调用服务器来内省 RPT?

不。与 Keycloak 服务器发出的常规访问令牌一样,RPT 也使用 JSON Web 令牌 (JWT) 规范作为默认格式。

如果您想在不调用远程内省端点的情况下验证这些令牌,您可以解码 RPT 并本地查询其有效性。解码令牌后,您还可以使用令牌中的权限来强制执行授权决策。

这本质上是策略执行器所做的事情。务必

  • 验证 RPT 的签名(基于领域的公钥)

  • 根据其 expiataud 声明查询令牌有效性

其他资源

策略执行器

策略执行点 (PEP) 是一种设计模式,因此您可以通过多种方式实现它。Keycloak 提供了所有必要的工具来为不同的平台、环境和编程语言实现 PEP。Keycloak 授权服务提供了一个 RESTful API,并利用 OAuth2 授权功能,使用集中式授权服务器进行细粒度授权。

Keycloak 提供的策略执行器包括

策略执行器的 JavaScript 集成

Keycloak 服务器附带一个 JavaScript 库,您可以使用它与受策略执行器保护的资源服务器进行交互。此库基于 Keycloak JavaScript 适配器,可以将其集成以允许您的客户端从 Keycloak 服务器获得权限。

您可以通过 从 NPM 安装 来获得此库

npm install keycloak-js

接下来,您可以创建 KeycloakAuthorization 实例,如下所示

import Keycloak from "keycloak-js";
import KeycloakAuthorization from "keycloak-js/authz";

const keycloak = new Keycloak({
    url: "http://keycloak-server",
    realm: "my-realm",
    clientId: "my-app"
});

const authorization = new KeycloakAuthorization(keycloak);

await keycloak.init();

// Now you can use the authorization object to interact with the server.

keycloak-js/authz 库提供了两个主要功能

  • 如果要访问受 UMA 保护的资源服务器,请使用权限票证从服务器获取权限。

  • 通过发送应用程序要访问的资源和范围来从服务器获取权限。

在这两种情况下,该库都允许您轻松地与资源服务器和 Keycloak 授权服务交互,以获取带有权限的令牌,您的客户端可以使用这些令牌作为承载令牌来访问资源服务器上的受保护资源。

处理来自受 UMA 保护的资源服务器的授权响应

如果资源服务器受策略执行器保护,它将根据附带在承载令牌中的权限来响应客户端请求。通常,当您尝试使用缺少访问受保护资源权限的承载令牌访问资源服务器时,资源服务器将以 **401** 状态码和 WWW-Authenticate 标头进行响应。

HTTP/1.1 401 Unauthorized
WWW-Authenticate: UMA realm="${realm}",
    as_uri="https://${host}:${port}/realms/${realm}",
    ticket="016f84e8-f9b9-11e0-bd6f-0021cc6004de"

有关更多信息,请参阅 UMA 授权流程

您的客户端需要做的是从资源服务器返回的 WWW-Authenticate 标头中提取权限票证,并使用库发送授权请求,如下所示

// prepare a authorization request with the permission ticket
const authorizationRequest = { ticket };

// send the authorization request, if successful retry the request
authorization.authorize(authorizationRequest).then((rpt) => {
    // onGrant
}, () => {
    // onDeny
}, () => {
    // onError
});

authorize 函数是完全异步的,支持一些回调函数来接收来自服务器的通知

  • onGrant:函数的第一个参数。如果授权成功且服务器返回了具有请求权限的 RPT,则回调将接收 RPT。

  • onDeny:函数的第二个参数。仅当服务器拒绝授权请求时才会调用。

  • onError:函数的第三个参数。仅当服务器以意外方式响应时才会调用。

大多数应用程序应该使用 onGrant 回调在 401 响应后重试请求。后续请求应该将 RPT 作为承载令牌包含在重试中。

获取权限

keycloak-js/authz 库提供了一个 entitlement 函数,您可以使用它通过提供客户端想要访问的资源和范围来从服务器获取 RPT。

获取具有用户可以访问的所有资源和范围的权限的 RPT 的示例
authorization.entitlement("my-resource-server-id").then((rpt) => {
    // onGrant callback function.
    // If authorization was successful you'll receive an RPT
    // with the necessary permissions to access the resource server
});
获取具有特定资源和范围的权限的 RPT 的示例
authorization.entitlement("my-resource-server", {
    permissions: [
        {
            id: "Some Resource"
        }
    ]
}).then((rpt) => {
    // onGrant
});

使用 entitlement 函数时,您必须提供要访问的资源服务器的 client_id

entitlement 函数是完全异步的,支持一些回调函数来接收来自服务器的通知

  • onGrant:函数的第一个参数。如果授权成功且服务器返回了具有请求权限的 RPT,则回调将接收 RPT。

  • onDeny:函数的第二个参数。仅当服务器拒绝授权请求时才会调用。

  • onError:函数的第三个参数。仅当服务器以意外方式响应时才会调用。

授权请求

authorizeentitlement 函数都接受授权请求对象。此对象可以使用以下属性进行设置

  • permissions

    表示资源和范围的对象数组。例如

    const authorizationRequest = {
       permissions: [
           {
               id: "Some Resource",
               scopes: ["view", "edit"]
           }
       ]
    }
  • 元数据

    一个对象,其属性定义了授权请求应如何由服务器处理。

    • response_include_resource_name

      一个布尔值,指示服务器是否应在 RPT 的权限中包含资源名称。如果为 false,则仅包含资源标识符。

    • response_permissions_limit

      一个整数 N,它定义了 RPT 可以拥有的权限数量的限制。当与 rpt 参数一起使用时,只有最后 N 个请求的权限将保留在 RPT 中

  • submit_request

    一个布尔值,指示服务器是否应为权限票证引用的资源和范围创建权限请求。此参数仅在与 ticket 参数一起使用作为 UMA 授权过程的一部分时才会生效。

获取 RPT

如果您已经使用库提供的任何授权函数获得了 RPT,则始终可以从授权对象中获得 RPT,如下所示(假设它已通过前面显示的技术之一初始化)

const rpt = authorization.rpt;