授权服务概述

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 中的 Class 一样),也可以代表单个特定资源。

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

可以使用 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. 客户端身份验证切换为开启

  6. 单击保存

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

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

    客户端设置

    Client Settings

启用授权服务

您可以将 OIDC 客户端转换为资源服务器并启用细粒度授权。

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

  2. 授权已启用切换为开启

  3. 单击保存

    启用授权服务

    Enabling authorization services

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

    资源服务器设置

    Resource server settings

授权选项卡包含其他子选项卡,涵盖了您必须遵循的实际保护应用程序资源的不同步骤。每个选项卡都在本文档的特定主题中单独介绍。但以下是关于每个选项卡的快速描述

  • 设置

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

  • 资源

    资源

  • 在此页面中,您可以管理应用程序的 资源

    授权范围

  • 在此页面中,您可以管理 范围

    策略

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

    权限

  • 在此页面中,您可以通过将 权限 与您创建的策略链接起来,来管理受保护资源和范围的权限。

    评估

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

    导出设置

资源服务器设置

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

  • 策略执行模式

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

    • 强制执行

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

    • 许可

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

    • 禁用

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

  • 决策策略

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

  • 远程资源管理

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

默认配置

当您创建资源服务器时,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

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

  • 类型

  • URIS

  • 所有者

  • 关联范围(如果有)

  • 关联权限

从此列表中,您还可以通过单击要为其创建权限的资源的 创建权限 来直接创建权限。

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

创建资源

创建资源是直接且通用的。您主要关注的是您创建的资源的粒度。换句话说,可以创建资源来表示一个或多个资源的集合,并且您定义它们的方式对于管理权限至关重要。

要创建新资源,请单击 创建资源

添加资源

Add resource

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

  • 名称

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

  • 类型

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

  • URIS

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

  • 范围

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

资源属性

资源可能具有与之关联的属性。这些属性可用于提供有关资源的附加信息,并在评估与资源关联的权限时为策略提供附加信息。

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

类型化资源

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

资源所有者

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

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

远程管理资源

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

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

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

管理策略

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

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

    在此页面中,您可以管理 范围

    Policies

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

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

本节描述了每种策略类型的详细信息。

基于用户的策略

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

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

添加用户策略

Add User Policy

配置

  • 名称

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

  • 描述

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

  • 用户

    指定哪些用户被此策略授予访问权限。

  • 逻辑

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

其他资源

基于角色的策略

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

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

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

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

添加角色策略

Add Role Policy

配置

  • 名称

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

  • 描述

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

  • 领域角色

    指定此策略允许的 领域 角色。

  • 客户端角色

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

  • 获取角色

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

  • 逻辑

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

其他资源

将角色定义为必需

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

必需角色的示例

Example of a required role

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

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

基于 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

配置

  • 名称

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

  • 描述

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

  • 客户端

    指定哪些客户端通过此策略获得 Group-based 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

客户端的 IP 地址,如果未提供有效的 IP,则可以为空。

字符串

kc.client.network.host

客户端的主机名,将是 IP 地址或代理标头提供的任何内容

字符串

kc.client.id

客户端 ID

字符串

kc.client.user_agent

'User-Agent' HTTP 标头的值

字符串数组

kc.realm.name

Realm 的名称

字符串

管理权限

权限关联受保护的对象和必须评估的策略,以决定是否应授予访问权限。

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

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

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-name}/.well-known/uma2-configuration

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

因此,您应该得到如下响应

{

    // 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-name}/protocol/openid-connect/token",
    "token_introspection_endpoint": "http://${host}:${port}/realms/${realm-name}/protocol/openid-connect/token/introspect",
    "resource_registration_endpoint": "http://${host}:${port}/realms/${realm-name}/authz/protection/resource_set",
    "permission_endpoint": "http://${host}:${port}/realms/${realm-name}/authz/protection/permission",
    "policy_endpoint": "http://${host}:${port}/realms/${realm-name}/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 参数一起使用时,只有最后 N 个请求的权限将保留在 RPT 中。

  • 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-name}/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-name}/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-name}/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}",
}

来自服务器的响应与使用其他授权类型时来自令牌端点的任何其他响应一样。可以从 access_token 响应参数中获取 RPT。如果客户端未获得授权,则 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 授权类型时,客户端可以使用以下任何一种身份验证方法

  • Bearer 令牌

    客户端应在 HTTP Authorization 标头中将访问令牌作为 Bearer 凭据发送到令牌端点。

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

    当客户端代表用户执行操作时,此方法特别有用。在这种情况下,Bearer 令牌是先前由 Keycloak 颁发给代表用户(或代表自身)执行操作的某些客户端的访问令牌。将考虑由访问令牌表示的访问上下文来评估权限。例如,如果访问令牌颁发给代表用户 A 执行操作的客户端 A,则将根据用户 A 有权访问的资源和范围授予权限。

  • 客户端凭据

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

    示例:使用客户端 ID 和客户端密钥向令牌端点进行身份验证的授权请求
    curl -X POST \
      http://${host}:${port}/realms/${realm-name}/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-name}/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

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

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

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

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

客户端向令牌端点发送授权请求以获取 RPT
curl -X POST \
  http://${host}:${port}/realms/${realm-name}/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-name}/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 以及如何获取它

保护 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 \
  https://#: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 \
  https://#: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 \
  https://#: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 \
  https://#: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 \
  https://#: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 \
  https://#:8180/realms/photoz/authz/protection/uma-policy/{permission_id} \
  -H 'Authorization: Bearer '$access_token
查询权限

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

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

要查询给定名称的权限,请发送如下所示的 HTTP GET 请求

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

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

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

要查询所有权限,请发送如下所示的 HTTP GET 请求

http://${host}:${port}/realms/${realm-name}/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}' \
    "https://#: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 的签名(基于 realm 的公钥)

  • 根据其 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-name}",
    as_uri="https://${host}:${port}/realms/${realm-name}",
    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"]
           }
       ]
    }
  • metadata

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

    • 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;