跳到主要内容
版本:4.1

OAuth 2.0 身份验证示例

概述

本教程风格的指南有两个主要目标

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

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

目录

基础知识

管理 UI 访问

在多种协议中使用 JWT 令牌访问 RabbitMQ

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

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

Tanzu RabbitMQ 示例(仅限商业功能)

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

  • 必须安装 Docker
  • 必须安装 Ruby
  • make
  • GitHub 存储库分支 next 的本地克隆,其中包含本示例中使用的所有配置文件和脚本。
  • 以下条目必须在您的 /etc/hosts 文件中
127.0.0.1 localhost uaa rabbitmq

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 时,它会显示一个标签为“Click here to logon”的按钮。当用户点击它时,登录过程开始,重定向到配置的授权服务器
  • 身份提供商发起的登录:此模式与之前的模式相反。用户必须首先使用令牌访问 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
提示

首次访问 https://uaa:8443,以便您的浏览器可以信任 uua 具有的自签名证书。否则,管理 UI 将无法连接到 uaa

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

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

JWT token

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

# ...
management.oauth_enabled = true
management.oauth_client_id = rabbit_client_code
auth_oauth2.issuer = https://uaa:8443
# ...
提示

当您未配置 auth_oauth2.issuer 和/或它们具有不同的 URL 时,您只需要设置 management.oauth_provider_url

身份提供商发起的登录

与服务提供商发起的登录一样,通过 Idp 发起的登录,用户可以使用有效的令牌访问 RabbitMQ 管理 UI。以下场景是 Idp 发起的登录的示例

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

后者场景在此处演示。前者的场景在以下部分中介绍。

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

Web 门户为其已身份验证的用户提供导航到 RabbitMQ 的选项,方法是在 access_token 表单字段中使用 OAuth 令牌提交表单,如下所示

    [ Idp | WebPortal ] ----> 2. /login [access_token: TOKEN]----   [ RabbitMQ Cluster ]
/|\ |
| |
1. rabbit_admin from a browser <-----3. 302 redirect to RabbitMQ w/cookie--+

如果访问令牌有效,RabbitMQ 会将用户重定向到概述页面,并附带一个包含已验证令牌的 cookie。当 RabbitMQ 传递概述页面时,它会清除 cookie。

默认情况下,RabbitMQ 管理 UI 配置为服务提供商发起的登录,要配置身份提供商发起的登录,需要在 rabbitmq.conf 中配置以下配置条目

# ...
management.oauth_enabled = true
management.oauth_initiated_logon_type = idp_initiated
management.oauth_provider_url = http://localhost:8080
# ...
提示

当您未配置 auth_oauth2.issuer 和/或它们具有不同的 URL 时,您只需要设置 management.oauth_provider_url

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

此场景在此处演示。

在多种协议中使用 JWT 令牌访问 RabbitMQ

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

管理 REST api

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

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

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

以下命令使用 mgt_api_client 客户端和一个先前从 UAA 获取的 JWT 令牌启动浏览器

make curl-uaa url=http://localhost:15672/api/overview client_id=mgt_api_client secret=mgt_api_client

AMQP 协议

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

相反,您正在使用您先前从 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 client grant flow 来获取 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) 读取将在与 RabbitMQ 进行身份验证时用作密码的 JWT 令牌。

至关重要的是,授予对交换 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:*/*/* 表示允许在任何 vhost、任何交换和任何主题上执行写入操作。实际上,它是任何“路由键”,因为它被转换为主题/队列。

您将通过运行以下命令发布 mqtt 消息。如果您尚未运行任何先前的用例,则需要首先启动 rabbitmq,如下所示 make start-rabbitmq

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

重要提示:如果您尝试访问管理 UI 并使用 rabbit_admin 通过 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 中声明的 client_id jms_producer 来获取令牌

make start-amqp1_0-publisher

使用主题交换

本节解释了使用主题交换的应用程序需要哪些范围。

重要提示

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

要将队列绑定和/或取消绑定到/从主题交换,您需要具有以下范围

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

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

例如,如果用户使用以下令牌连接到 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=http://localhost: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=http://localhost: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 一起使用。

UAA 身份提供商已配置了两个客户端(producer_with_rolesconsumer_with_roles),具有以下自定义范围

producer_with_roles 具有

  • api://rabbitmq:producer.

consumer_with_roles 具有

  • api://rabbitmq:Read.All.
  • api://rabbitmq:Write.All.
  • api://rabbitmq:Configure.All.
  • api://rabbitmq:Administrator.

有关范围别名的更多信息,请查看部分,其中更详细地解释了它。

如何配置范围别名

这是将这些自定义范围映射到 RabbitMQ 范围所需的配置。

提示

自 RabbitMQ 4.1 起,可以使用 ini-like 配置样式配置范围别名。早期版本仅支持传统的 Erlang 样式。

auth_oauth2.scope_aliases.1.alias = api://rabbitmq:Read.All
auth_oauth2.scope_aliases.1.scope = rabbitmq.read:*/*

auth_oauth2.scope_aliases.2.alias = api://rabbitmq:Write.All
auth_oauth2.scope_aliases.2.scope = rabbitmq.write:*/*

auth_oauth2.scope_aliases.3.alias = api://rabbitmq:Configure.All
auth_oauth2.scope_aliases.3.scope = rabbitmq.configure:*/*

auth_oauth2.scope_aliases.3.alias = api://rabbitmq:Administrator
auth_oauth2.scope_aliases.3.scope = rabbitmq.tag:administrator

auth_oauth2.scope_aliases.4.alias = api://rabbitmq:producer
auth_oauth2.scope_aliases.4.scope = rabbitmq.read:*/* rabbitmq.write:*/* rabbitmq.configure:*/* rabbitmq.tag:management

RabbitMQ 配置

在 OAuth 2.0 教程存储库中,有两个 RabbitMQ 配置文件已准备好使用,用于 UAA

演示 1:启动在范围字段中具有自定义范围的 RabbitMQ

要启动具有范围映射并在 scope 字段中具有自定义范围的 RabbitMq,您运行以下命令

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

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

使用客户端 producer_with_roles 启动 producer 应用程序

make start-perftest-producer PRODUCER=producer_with_roles

检查日志

docker logs producer_with_roles -f

使用客户端 consumer_with_roles 启动 consumer 应用程序

make start-perftest-consumer CONSUMER=consumer_with_roles

查看日志:docker logs consumer_with_roles -f

使用客户端 producer_with_roles 访问管理 api

make curl url=http://localhost:15672/api/overview client_id=producer_with_roles secret=producer_with_roles_secret

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

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

演示 2:启动在额外范围字段中具有自定义范围的 RabbitMQ

要启动具有范围映射并在 extra_scope 中具有自定义范围的 RabbitMq,您运行以下命令

make start-rabbitmq

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

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

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

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 启动 consumer 应用程序

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="http://localhost: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 配置的相同的私钥-公钥对对其进行签名。

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

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

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

注意:您正在使用 curl 转到 URL,使用 TOKEN,该 TOKEN 是您使用命令 bin/jwt_token 构建的,该命令采用 JWT 有效负载、签名密钥的名称以及用于签名令牌的私钥和公钥证书

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

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

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 指南

© . All rights reserved.