跳至主要内容
版本:4.0

OAuth 2.0 身份验证示例

概述

本教程式指南有两个主要目标

  1. 探索应用程序和最终用户如何使用 OAuth 2.0 而不是传统的用户名/密码对或 x.509 证书 进行身份验证 与 RabbitMQ 服务器。
  2. 探索在多个授权服务器之间设置使用 OAuth 2.0 身份验证机制的 RabbitMQ 服务器需要什么。

本指南附带了一个 公开的 GitHub 存储库,其中托管了在指南中演示的所有示例所需的脚本。

目录

基础

管理 UI 访问

使用 各种协议使用 JWT 令牌

签名密钥、范围别名、丰富授权请求

特定 OAuth 2.0 身份提供商的示例

本指南中示例使用的先决条件

  • 必须安装 Docker
  • 必须安装 Ruby
  • make
  • git clone https://github.com/rabbitmq/rabbitmq-oauth2-tutorial。此 GitHub 存储库包含所有示例中使用的所有配置文件和脚本。

UAA 和 RabbitMQ 入门

为了演示 OAuth 2.0,您至少需要一个 OAuth 2.0 授权服务器和为所选授权服务器适当配置的 RabbitMQ。本指南使用 UAA 作为授权服务器,以演示访问管理 UI 和各种消息传递协议的基本和高级配置。

本指南还演示了如何配置 RabbitMQ 以使用除 UAA 之外的其他授权服务器,例如 Keycloak。本指南的目录包含所有授权服务器的完整列表。

运行以下两个命令以启动为 UAA 配置的 UAA 和 RabbitMQ

  1. make start-uaa 以启动 UAA 服务器
  2. make start-rabbitmq 以启动 RabbitMQ 服务器

最后一个命令启动一个使用特定配置文件的 RabbitMQ,rabbitmq.conf

使用 OAuth 2.0 令牌访问 管理 UI

RabbitMQ 管理 UI 可以使用以下两种登录模式之一进行配置

  • 服务提供商发起的登录:这是默认的和传统的 OAuth 2.0 登录模式。当用户访问 RabbitMQ 管理 UI 时,它会显示一个带有标签 点击此处登录 的按钮。当用户点击它时,登录过程将通过重定向到配置的 授权服务器 开始。
  • 身份提供商发起的登录:此模式与上一种模式相反。用户必须首先使用令牌访问 RabbitMQ 管理的 /login 端点。如果令牌有效,则允许用户访问 RabbitMQ 管理 UI。此模式对于允许用户单击一次即可访问 RabbitMQ 管理 UI 的网站非常有用。原始网站代表用户获取令牌并将用户重定向到 RabbitMQ 管理的 /login 端点。

服务提供商发起的登录

最终用户首次访问管理 UI 时,他们会被重定向到配置的 OAuth 2.0 提供商以进行身份验证。成功验证后,用户会被重定向回 RabbitMQ,并携带有效的访问令牌。RabbitMQ 会验证它,并从令牌中识别用户及其权限。

    [ UAA ] <----2. auth----    [ RabbitMQ ]
----3. redirect--> [ http ]
/|\
|
1. rabbit_admin from a browser

在步骤 2 中,如果这是用户首次访问 RabbitMQ 资源,UAA 会提示用户授权 RabbitMQ 应用程序,如下面的屏幕截图所示。

authorize application

UAA 之前已配置并播种了两个用户

  • rabbit_admin:rabbit_admin
  • 以及 rabbit_monitor:rabbit_monitor

现在,导航到 本地节点的管理 UI 并使用这两个用户中的任何一个登录。

这是 UAA 为 rabbit_admin 用户通过您刚刚看到的重定向流程发出的令牌。它是使用对称密钥签名的。

JWT token

要使用 OAuth 2.0 配置 RabbitMQ 管理 UI,需要在 rabbitmq.conf 中添加以下配置项

# ...
management.oauth_enabled = true
management.oauth_client_id = rabbit_client_code
management.oauth_provider_url = https://127.0.0.1:8080
# ...

身份提供商发起的登录

与服务提供商发起的登录一样,使用 Idp 发起的登录,用户会带着有效令牌进入 RabbitMQ 管理 UI。以下场景是 Idp 发起的登录的示例

  • RabbitMQ 位于一个 Web 门户后面,该门户方便地允许用户直接导航到完全经过身份验证的 RabbitMQ。
  • 用户和 RabbitMQ 之间存在一个 OAuth2 代理,它会拦截用户的请求并将它们转发到 RabbitMQ,将令牌插入 HTTP Authorization 标头中。

后一种场景已在 此处 演示。前一种场景将在下一节中介绍。

使用登录端点的 Idp 发起的登录

Web 门户为其经过身份验证的用户提供了导航到 RabbitMQ 的选项,方法是提交一个包含其 OAuth 令牌的表单,该令牌位于 access_token 表单字段中,如下所示

    [ Idp | WebPortal ] ----> 2. /login [access_token: TOKEN]----   [ RabbitMQ Cluster ]
/|\ | /|\
| +--------+
1. rabbit_admin from a browser 3. validate token

如果访问令牌有效,RabbitMQ 会将用户重定向到 概述 页面。

默认情况下,RabbitMQ 管理 UI 使用 服务提供商发起的登录 进行配置,要配置 身份提供商发起的登录,需要在 rabbitmq.conf 中添加以下配置项

# ...
management.oauth_enabled = true
management.oauth_client_id = rabbit_client_code
management.oauth_provider_url = https://127.0.0.1:8080
management.oauth_initiated_logon_type = idp_initiated
# ...

重要:当用户注销或其 RabbitMQ 会话过期或令牌过期时,用户会被重定向到 RabbitMQ 管理登录页面,该页面有一个 点击此处登录 按钮。用户永远不会自动重定向回 oauth_provider_url 中配置的 URL。只有当用户点击 点击此处登录 时,用户才会被重定向到 oauth_provider_url 中配置的 URL。

使用 OAuth 2.0 令牌访问其他协议

以下小节将演示如何使用访问令牌与任何消息传递协议以及访问管理 REST API。

管理 REST API

在此场景中,监控代理使用 RabbitMQ HTTP API 收集监控信息。因为它不是最终用户或人类,所以您将其称为 服务帐户。此 服务帐户 可以是您在 UAA 中创建的带有 monitoring 用户标签mgt_api_client 客户端。

监控代理 将使用 客户端凭据密码 授予流程进行身份验证 (1) 与 UAA,并获取 JWT 令牌 (2)。获得令牌后,它会向 RabbitMQ 管理端点发送 (3) HTTP 请求,将 JWT 令牌作为 Bearer 令牌 传递到 Authorization 标头中。

[ UAA ]                  [ RabbitMQ ]
/|\ [ http ]
| /|\
| 3.https://broker:15672/api/overview passing JWT token
| |
+-----1.auth--------- monitoring agent
--------2.JWT-------->

以下命令将使用以前从 UAA 获取的 JWT 令牌启动带有 mgt_api_client 客户端的浏览器

make curl-uaa url=https://127.0.0.1:15672/api/overview client_id=mgt_api_client secret=mgt_api_client

AMQP 协议

应用程序使用 AMQP 协议连接到 RabbitMQ,并提供 JWT 令牌作为凭据。您将要使用的应用程序是 PerfTest,它不是一个支持 OAuth 2.0 的应用程序。支持 OAuth 2.0 的应用程序将在场景 4 中介绍。

相反,您将使用您以前从 UAA 获取的令牌启动应用程序。这只是为了用 JWT 令牌探测 AMQP 访问。毋庸置疑,应用程序应该在连接到 RabbitMQ 之前获取 JWT 令牌,并且它还应该能够在重新连接之前刷新令牌。RabbitMQ 会在接受令牌之前验证令牌。如果令牌已过期,RabbitMQ 会拒绝连接。

首先,想要使用 Oauth 2.0 连接到 RabbitMQ 的应用程序必须提供一个有效的 JWT 令牌。要获取令牌,应用程序必须首先进行身份验证 (1.) 与 UAA。如果身份验证成功,它会获取 JWT 令牌 (2.),并使用它连接 (3.) 到 RabbitMQ。

[ UAA ]                  [ RabbitMQ ]
/|\ [ amqp ]
| /|\
| 3.connect passing JWT
| |
+-----1.auth--------- amqp application
--------2.JWT-------->

您之前已经使用以下 2 个 OAuth 2.0 客户端配置了 UAA

  • consumer
  • producer

为了获取 JWT 令牌,必须使用 OAuth 2.0 客户端。应用程序使用`Oauth 客户端授权流程`来获取 JWT 令牌。

这是 UAA 为`consumer` OAuth 2.0 客户端发行的令牌。

JWT token

要启动 consumer 应用程序,请调用以下命令

make start-perftest-consumer

要查看 consumer 日志

docker logs consumer -f

要启动 producer 应用程序,请调用以下命令

make start-perftest-producer

要检查 producer 日志

docker logs producer -f

要停止所有应用程序,请调用以下命令

make stop-all-apps

JMS 协议

在这个用例中,您将演示一个基本的 JMS 应用程序,它通过环境变量 (TOKEN) 读取 JWT 令牌,并将该令牌用作身份验证 RabbitMQ 时的密码。

至关重要的是,要为交换 jms.durable.queues 授予所需的权限。

发送 JMS 消息的应用程序需要这些权限

  • rabbitmq.configure:*/jms.durable.queues
  • rabbitmq.write:*/jms.durable.queues
  • rabbitmq.read:*/jms.durable.queues

这些权限授予对所有虚拟主机访问。

在测试发布者和订阅者应用程序之前,您需要通过调用以下命令构建基本 jms 应用程序的本地镜像

make build-jms-client

要测试通过 OAuth 2.0 发送消息并进行身份验证的 JMS 应用程序,请运行此命令

make start-jms-publisher

它将消息发送到名为q-test-queue 的队列

订阅 JMS 队列的应用程序需要这些权限

  • rabbitmq.write:*/jms.durable.queues

这些权限授予对所有虚拟主机访问。

要测试通过 OAuth 2.0 订阅队列并进行身份验证的 JMS 应用程序,请运行此命令

make start-jms-subscriber

它订阅名为q-test-queue 的队列

MQTT 协议

此场景探讨了使用 JWT 令牌对 RabbitMQ MQTT 端口进行身份验证的用例。

注意:在此示例中,RabbitMQ 已使用 rabbitmq_mqtt 插件 进行配置。

这与使用 AMQP 或 JMS 协议没有区别,重要的是要传递一个空用户名和一个 JWT 令牌作为密码。但是,真正不同的是您如何对权限进行编码。在这个用例中,您将按照之前手动制作 JWT 令牌而不是从 UAA 请求令牌的方式进行操作。以下是发布消息到 mqtt 主题所需的范围 (scopes-for-mqtt.json)

{
"scope": [
"rabbitmq.write:*/*/*",
"rabbitmq.configure:*/*/*",
"rabbitmq.read:*/*/*"

],
"extra_scope": "rabbitmq.tag:management",
"aud": [
"rabbitmq"
]
}

rabbitmq.write:*/*/* 表示允许对任何虚拟主机、任何交换机和任何主题进行写入操作。实际上,它是任何“路由键”,因为它被转换为主题/队列。

您将通过运行以下命令来发布 mqtt 消息。如果您之前没有运行任何用例,则需要先启动 rabbitmq,例如 make start-rabbitmq

make start-mqtt-publish TOKEN=$(bin/jwt_token scopes-for-mqtt.json legacy-token-key private.pem public.pem)

重要:如果您尝试使用 rabbit_admin 访问管理 UI 并使用 UAA 进行身份验证,您将无法将队列与路由键test 绑定到amq.topic 交换机,因为 UAA 中的用户没有所需的权限。在我们手工制作的令牌中,您已为自己授予了正确的权限/范围。

AMQP 1.0 协议

在这个用例中,您将演示一个基本的 AMQP 1.0 应用程序,它从PASSWORD 环境变量中读取 JWT 令牌。然后,应用程序在身份验证 RabbitMQ 时使用该令牌作为密码。

在测试发布者和订阅者应用程序之前,您需要通过调用以下命令构建基本 AMQP 1.0 应用程序的本地镜像

make build-amqp1_0-client

使用以下命令启动 RabbitMQ。它将启动配置了 UAA 作为其授权服务器的 RabbitMQ

make start-rabbitmq

启动 UAA

make start-uaa

并发送消息。它使用 UAA 中声明的客户端 ID jms_producer 来获取令牌

make start-amqp1_0-publisher

使用主题交换机

本节介绍使用主题交换机的应用程序所需的范围。

重要

在本教程提供的任何授权服务器中声明的用户和/或客户端都没有主题交换机所需的适当范围。在 MQTT 部分,应用程序使用具有适当范围的手工制作的令牌,因为该协议的路由完全基于主题。

要将队列绑定到主题交换机或从主题交换机解除绑定,您需要具有以下范围

对于消费者
权限示例
对队列和路由键的写入权限 -> rabbitmq.write:<vhost>/<queue>/<routingkey>rabbitmq.write:*/*/*
对交换机和路由键的读取权限 -> rabbitmq.write:<vhost>/<exchange>/<routingkey>rabbitmq.read:*/*/*
对于发布者
权限示例
对交换机和路由键的写入权限 -> rabbitmq.write:<vhost>/<exchange>/<routingkey>rabbitmq.write:*/*/*

OAuth 2.0 授权后端支持在检查主题权限时进行变量扩展。它支持任何值是纯字符串的 JWT 声明和vhost 变量。

例如,如果用户使用以下令牌连接到虚拟主机prod,他们应该具有对以x-prod- 开头的任何交换机和以u-bob- 开头的任何路由键进行写入的权限

{
"sub" : "bob",
"scope" : [ "rabbitmq.write:*/q-{vhost}-*/u-{sub}-*" ]
}

高级 OAuth 2.0 配置主题

使用自定义范围字段

有些授权服务器无法将 RabbitMQ 范围包含在标准 JWT scope 字段中。相反,它们可以将 RabbitMQ 范围包含在其选择的自定义 JWT 范围中。

可以使用不同的字段来配置 RabbitMQ 以查找范围,如下所示

...
auth_oauth2.additional_scopes_key = extra_scope
...

要测试此功能,您将构建一个令牌、对其进行签名并使用它来访问 RabbitMQ 的一个管理端点。以下命令允许我们使用令牌访问任何管理端点,在本例中为overview

make curl-with-token URL=https://127.0.0.1:15672/api/overview TOKEN=$(bin/jwt_token scope-and-extra-scope.json legacy-token-key private.pem public.pem)

您使用 python 脚本 bin/jwt_token.py 来构建 RabbitMQ 能够验证的最小的 JWT 令牌,它是

{
"scope": [

],
"extra_scope": [
"rabbitmq.tag:management"
],
"aud": [
"rabbitmq"
]
}

使用多个非对称签名密钥

此场景探讨了 JWT 令牌可能由不同的非对称签名密钥签名的用例。

有两种方法可以使用多个签名密钥来配置 RabbitMQ

  • 静态地通过rabbitmq.conf 进行配置,如 插件文档页面 中所示。
  • 动态地将密钥添加到正在运行的 RabbitMQ 节点,而无需重新启动。

首先,您添加一个名为legacy-token-2-key 的第二个签名密钥,其公钥为conf/public-2.pem

docker exec -it rabbitmq rabbitmqctl add_signing_key legacy-token-2-key --pem-file=/conf/public-2.pem
Adding OAuth signing key "legacy-token-2-key" filename: "/conf/public-2.pem"

然后,您使用相应的私钥颁发令牌,并使用它访问管理端点/api/overview

make curl-with-token URL=https://127.0.0.1:15672/api/overview TOKEN=$(bin/jwt_token scope-and-extra-scope.json legacy-token-2-key private-2.pem public-2.pem)

bin/jwt_tokenconf 目录下搜索私钥和公钥文件,并在jwts 下搜索 jwt 文件。

使用范围别名

在这个用例中,您将演示如何配置 RabbitMQ 来处理自定义范围。但是自定义范围是什么?它们是任何格式不符合 RabbitMQ 格式的范围。例如,api://rabbitmq:Read.All 是您将在本用例中使用的自定义范围之一。

如何配置 RabbitMQ 以使用自定义范围映射

RabbitMQ 3.10.0 开始,OAuth 2.0 插件支持将范围别名(任意范围值或“名称”)映射到一个或多个格式符合 RabbitMQ OAuth 2.0 插件约定的范围。

以下是一个示例 RabbitMQ 配置,其中您将api://rabbitmq:Read.All 自定义范围映射到rabbitmq.read:*/* RabbitMQ 范围。

{rabbitmq_auth_backend_oauth2, [
%%...,
{scope_aliases, #{
<<"api://rabbitmq:Read.All">> => [<<"rabbitmq.read:*/*">>],
...
},
%%...
]}

此外,您可以将自定义范围映射到多个 RabbitMQ 范围。例如,在下面,您将角色api://rabbitmq:producer 映射到 3 个 RabbitMQ 范围,这将在任何资源和任何虚拟主机上授予readwriteconfigure 访问权限

{rabbitmq_auth_backend_oauth2, [
%% ...,

{scope_aliases, #{
<<"api://rabbitmq:producer">> => [
<<"rabbitmq.read:*/*">>,
<<"rabbitmq.write:*/*">>,
<<"rabbitmq.configure:*/*">>
]
}},
%% ...
]}

JWT 令牌中的范围别名

如果您没有使用extra_scopes_source 配置 RabbitMQ OAuth 2.0 插件,则 RabbitMQ 期望scope 令牌字段携带自定义范围。例如,下面是一个示例 JWT 令牌,其中自定义范围位于scope 字段中

{
"sub": "producer",
"scope": [
"api://rabbitmq:producer",
"api://rabbitmq:Administrator"
],
"aud": [
"rabbitmq"
]
}

现在,假设您使用以下方法配置了 RabbitMQ OAuth 2.0 插件,使用extra_scopes_source

# ...
auth_oauth2.resource_server_id = rabbitmq
auth_oauth2.additional_scopes_key = roles
# ...

使用此配置,RabbitMQ 期望roles 字段中的自定义范围,并且scope 字段将被忽略。

{
"sub": "rabbitmq-client-code",
"roles": "api://rabbitmq:Administrator.All",
"aud": [
"rabbitmq"
]
}

UAA 配置

为了演示这种新功能,您已使用两个 Oauth 2.0 客户端配置了 UAA。一个名为producer_with_roles,带有自定义范围api://rabbitmq:producer,另一个名为consumer_with_roles,带有api://rabbitmq:Read:All,api://rabbitmq:Configure:All,api://rabbitmq:Write:All

您授予了消费者配置和写入权限,因为您已配置 perf-test 来声明资源,无论它是生产者还是消费者应用程序。

这两个 uaac 命令声明了上述两个 OAuth 2.0 客户端。您添加了一个名为rabbitmq.* 的额外范围,以便 UAA 使用值rabbitmq 填充 JWT 声明aud。RabbitMQ 期望aud 与您在resource_server_id 字段中配置的 RabbitMQ 值匹配。

uaac client add producer_with_roles --name producer_with_roles \
--authorities "rabbitmq.*,api://rabbitmq:producer,api://rabbitmq:Administrator" \
--authorized_grant_types client_credentials \
--secret producer_with_roles_secret
uaac client add consumer_with_roles --name consumer_with_roles \
--authorities "rabbitmq.* api://rabbitmq:read:All" \
--authorized_grant_types client_credentials \
--secret consumer_with_roles_secret

RabbitMQ 配置

在 OAuth 2.0 教程存储库中,有两个 RabbitMQ 配置文件,可以用于 UAA

演示 1:在 scope 字段中使用自定义范围启动 RabbitMQ

要使用范围映射并在scope字段中使用自定义范围启动 RabbitMq,请运行以下命令

ADVANCED=advanced-scope-aliases.config make start-rabbitmq

如果 RabbitMQ 正在运行,此命令将停止它。

使用客户端producer_with_roles启动生产者应用程序

make start-perftest-producer PRODUCER=producer_with_roles

要检查日志

docker logs producer_with_roles -f

使用客户端consumer_with_roles启动消费者应用程序

make start-perftest-consumer CONSUMER=consumer_with_roles

要检查日志:docker logs consumer_with_roles -f

使用客户端producer_with_roles访问管理 API

make curl url=https://127.0.0.1:15672/api/overview client_id=producer_with_roles secret=producer_with_roles_secret

要停止性能测试应用程序,请运行

make stop-perftest-producer PRODUCER=producer_with_roles
make stop-perftest-consumer CONSUMER=consumer_with_roles

演示 2:在 extra scope 字段中使用自定义范围启动 RabbitMQ

要使用范围映射并在extra_scope中使用自定义范围启动 RabbitMq,请运行以下命令

make start-rabbitmq

如果 RabbitMQ 正在运行,此命令将停止它

您不能使用 UAA 来颁发令牌,因为您无法配置 UAA 来使用自定义字段作为范围。相反,您将使用命令bin/jwt_token自己颁发令牌。

使用令牌producer-role-in-scope.json启动生产者应用程序

make start-perftest-producer-with-token PRODUCER=producer_with_roles TOKEN=$(bin/jwt_token producer-role-in-extra-scope.json legacy-token-key private.pem public.pem)

要检查日志

docker logs producer_with_roles -f

使用令牌consumer-roles-in-extra-scope.json启动消费者应用程序

make start-perftest-consumer-with-token CONSUMER=consumer_with_roles TOKEN=$(bin/jwt_token consumer-roles-in-extra-scope.json legacy-token-key private.pem public.pem)

使用令牌producer-roles-in-extra-scope.json访问管理 API

make curl-with-token URL="https://127.0.0.1:15672/api/overview" TOKEN=$(bin/jwt_token producer-roles-in-extra-scope.json legacy-token-key private.pem public.pem)

要停止perf-test应用程序,请运行

make stop-perftest-producer PRODUCER=producer_with_roles
make stop-perftest-consumer CONSUMER=consumer_with_roles

首选用户名声明

RabbitMQ 需要确定与令牌关联的用户名,以便它可以在管理 UI 中显示它。默认情况下,RabbitMQ 将首先查找sub声明,如果未找到,它将使用client_id

大多数授权服务器在sub声明中返回用户的 GUID,而不是用户的实际用户名或电子邮件地址,任何用户可以关联的任何内容。当sub声明不包含用户友好用户名时,您可以配置一个或多个声明以从令牌中提取用户名。

鉴于此配置

...
auth_oauth2.resource_server_id = rabbitmq
auth_oauth2.preferred_username_claims.1 = user_name
auth_oauth2.preferred_username_claims.2 = email
...

RabbitMQ 将首先查找user_name声明,如果未找到,它将查找email。否则,它将使用其默认查找机制,该机制首先查找sub,然后查找client_id

使用富授权请求令牌

富授权请求扩展提供了一种方法,使 OAuth 2.0 客户端能够在授权请求期间请求细粒度权限。它摆脱了作为文本标签的范围的概念,而是定义了一个更复杂的权限模型。

RabbitMQ 支持与该扩展兼容的 JWT 令牌。下面是 JWT 令牌的示例部分

{
"authorization_details": [
{ "type" : "rabbitmq",
"locations": ["cluster:finance/vhost:primary-*"],
"actions": [ "read", "write", "configure" ]
},
{ "type" : "rabbitmq",
"locations": ["cluster:finance", "cluster:inventory", ],
"actions": ["tag:administrator" ]
}
]
}

准备好环境

为了演示此新功能,您必须在conf/uaa/rabbitmq-for-rar-tokens.config下部署 RabbitMQ,并使用相应的配置文件。

export CONFIG=rabbitmq-for-rar-tokens.config
make start-rabbitmq

注意:您不需要运行任何 OAuth 2.0 服务器,例如 UAA。这是因为您正在使用与 RabbitMQ 配置的相同的私钥-公钥对创建令牌并对其进行签名。

使用富授权令牌访问管理 REST API

您将使用此令牌jwts/rar-token.json来访问管理 REST API 的端点。

make curl-with-token URL=https://127.0.0.1:15672/api/overview TOKEN=$(bin/jwt_token rar-token.json legacy-token-key private.pem public.pem)

注意:您正在使用 curl 使用您使用命令 bin/jwt_token 生成的令牌访问 URL,该命令采用 JWT 有效负载、签名密钥的名称以及私钥和公钥证书来对令牌进行签名

使用富授权令牌进行应用程序身份验证和授权

这次,您将使用上一节中使用的相同令牌通过 PerfTest 工具访问 AMQP 协议,该工具充当 AMQP 生产者应用程序

make start-perftest-producer-with-token PRODUCER=producer_with_roles TOKEN=$(bin/jwt_token rar-token.json legacy-token-key private.pem public.pem)

上面的命令在后台启动应用程序,您可以通过运行以下命令来检查日志

docker logs producer_with_roles -f

有关此新功能的更多信息,请查看OAuth 2 指南

© 2024 RabbitMQ. All rights reserved.