保护你的API资源用于机器与机器之间的通信
学习如何利用 OAuth 2.0 和 JWT 来保护你的API资源用于机器与机器之间的通信。
学习如何利用 OAuth 2.0 和 JWT 来保护你的API资源用于机器与机器之间的通信。
当构建涉及多个服务的项目时,API资源的安全性是一个关键问题。在这篇文章中,我将向你展示如何利用 OAuth 2.0 和 JWT 来保护服务之间的通信(机器与机器),以及如何应用基于角色的访问控制(RBAC)以遵循最低权限原则。
为了跟随本教程,我假设你有以下先决条件:
出于演示目的,假设我们有以下服务:
https://cart.example.com/apihttps://payment.example.com/api现在,我们的购物车服务需要调用支付服务来处理支付。认证流程如下:
上图中的一些关键概念:
上图中的每个参与者在认证流程中都有一个角色要扮演:
让我们逐步讲解认证流程。
要执行认证流程,我们需要在 Logto 中创建一个机器对机器应用程序(购物车服务)和一个API资源(支付服务)。
由于我们的购物车服务在执行认证时需要知道支付服务的API,我们首先需要创建一个API资源。前往 Logto 控制台,点击左侧边栏的API资源,然后点击创建API资源。在打开的对话框中,我们提供了一些教程帮助你入门。你也可以点击不使用教程继续跳过它。
输入API名称和标识符,例如Payment service和https://payment.example.com/api,然后点击创建API资源。
创建API资源后,你将被重定向到详细信息页面。我们现在可以暂时不做任何更改。
点击左侧边栏的应用程序,然后点击创建应用程序。在打开的对话框中,找到机器对机器卡片,然后点击开始构建。
输入应用程序名称,例如Cart service,然后点击创建应用程序。一个互动指南将会显示,帮助你设置应用程序。你可以跟随指南了解基本用法,或者点击完成并完成跳过它。
由于假设机器对机器应用程序是安全的(例如,它们部署在私人网络中),所以我们可以使用 OAuth 2.0 的“client_credentials”授权来获取访问令牌。它使用基本认证来认证客户端:
POST。Content-Type头是application/x-www-form-urlencoded。Authorization头,其值是Basic <base64(app_id:app_secret)>,其中app_id和app_secret分别是机器对机器应用程序的应用程序ID和应用程序密钥。你可以在应用程序详情页中找到它们。grant_type=client_credentials&resource=https://payment.example.com/api。
grant_type=client_credentials:"client_credentials"授权的常量值。resource=https://payment.example.com/api:客户端希望访问的API资源的API标识符。scope=read:payment write:payment。我们将在后面介绍权限。以下是使用curl的请求示例:
成功的响应体如下所示:
现在我们有了访问令牌,可以将其添加到对API资源请求的Authorization头中。例如,如果我们想调用支付服务的POST /payments API,可以发送以下请求:
你可能注意到,支付服务需要使用 JWK 集验证 JWT,并且可能有一个本地 JWK 集缓存以避免每次从 Logto 获取 JWK 集。幸运的是,由于 JWT 的流行,有许多库可以帮助你用几行代码实现目标。
这些库通常被称为“jose”(JavaScript对象签名和加密)或“jsonwebtoken”。例如,在 Node.js 中我们可以使用jose来验证 JWT:
如果验证成功,payload变量将是解码后的 JWT 负载。否则,将抛出一个错误。
现在我们已经成功保护了购物车服务和支付服务之间的通信。然而,认证流程仅确保客户端是实际的购物车服务,并不能确保购物车服务有任何权限执行支付服务上的操作。
假设我们想允许购物车服务创建支付,但不允许读取支付。
在 Logto 中,“范围”和“权限”是可以互换使用的。前往支付服务的API资源详情页面,并导航到权限选项卡。现在应该是空的。点击创建权限,输入read:payment作为权限名称,输入读取支付作为权限描述。然后点击创建权限。
重复上述步骤创建另一个名称为write:payment,描述为创建支付的权限。
角色是一组权限。在 Logto 中,机器对机器应用程序可以分配角色来授予权限。点击左侧边栏中的"角色",然后点击创建角色。
checkout作为角色名称,输入Checkout service作为角色描述。write:payment权限。


除了我们在请求访问令牌中提到的请求体参数,我们还可以在请求体中指定范围。例如,如果我们想请求write:payment权限, 可以发送以下请求:
要请求多个范围,你可以用空格分隔它们。例如,scope=write:payment read:payment。
如果某个操作需要支付服务中的write:payment权限,我们可以通过断言 JWT 负载的scope声明来验证范围:
如果你想保护对购物车服务的访问,也可以应用相同的认证流程。这一次,购物车服务是API资源,而客户端则是另一个需要访问它的服务。
使用 Logto,你的API资源通过 OAuth 2.0 和 JWT 得到保护,并且通过应用基于角色的访问控制,你可以遵循最低权限原则。此外,你还可以使用 Logto 管理你的用户及其权限,甚至与第三方身份提供者集成。