繁體中文(台灣)
  • oauth
  • jwt
  • api
  • security
  • authentication
  • authorization
  • machine-to-machine

保護 API 資源以實現機器對機器的通信

學習如何利用 OAuth 2.0 和 JWT 來保護你的 API 資源,以實現機器對機器的通信。

Gao
Gao
Founder

構建涉及多個服務的項目時,API 資源的安全性是一個關鍵問題。在本文中,我將向你展示如何利用 OAuth 2.0 和 JWT 來保護服務之間的通信(機器對機器),以及如何應用基於角色的訪問控制(RBAC)以遵循最低權限原則。

開始使用

為了便於學習,我假設你擁有以下先決條件:

  • 一個 Logto Cloud 帳號,或自行托管的 Logto 實例
  • 至少兩個需要相互通信的服務

出於演示目的,我們假設擁有以下服務:

  • 一個提供 API 用於管理購物車的購物車服務
    • 端點:https://cart.example.com/api
  • 一個提供 API 用於處理支付的支付服務
    • 端點:https://payment.example.com/api

驗證流程

現在,我們的購物車服務需要調用支付服務以處理支付。驗證流程如下:

上圖中一些關鍵概念:

  • JWT (RFC 7519):JSON Web Token。參見我們的上一篇文章,瞭解 JWT 的介紹。
  • JWK (RFC 7517):JSON Web Key 用於驗證 JWT 的簽名。JWK 集是 JWK 的集合。
  • "client_credentials" 授權 (RFC 6749):OAuth 2.0 中的一種授權類型。使用客戶端的憑據來獲取訪問令牌。我們會在後面的部分展示詳情。

以上圖中的每個參與者在驗證流程中都有其角色:

  • 購物車服務:需要調用支付服務的客戶端。雖然它是一個服務,但在 OAuth 2.0 的上下文中,它仍然是客戶端,我們在 Logto 中稱這樣的客戶端為"機器對機器應用程序"。
  • Logto:發放訪問令牌的 OAuth 2.0 授權服務器。
  • 支付服務:提供 API 用於處理支付的 API 資源。

讓我們一步一步地了解驗證流程。

初始設置

要執行驗證流程,我們需要在 Logto 中創建一個機器對機器應用程序(購物車服務)和一個 API 資源(支付服務)。

創建 API 資源

由於我們的購物車服務在執行驗證時需要知道支付服務的 API,因此我們需要首先創建一個 API 資源。進入 Logto 控制台,點擊左側邊欄中的 API 資源,然後點擊 創建 API 資源。在開啟的對話框中,我們提供了一些教程幫助你開始使用。你也可以點擊 繼續而不使用教程 以跳過它。

輸入 API 名稱和標識符,例如 Payment servicehttps://payment.example.com/api,然後點擊 創建 API 資源

創建 API 資源後,你會被重定向到詳細信息頁面。現在我們可以暫時不處理它。

創建機器對機器應用程序

點擊左側邊欄中的 應用程序,然後點擊 創建應用程序。在開啟的對話框中,找到 機器對機器 卡片,然後點擊 開始構建

輸入應用程序名稱,例如 Cart service,然後點擊 創建應用程序。將顯示一個互動指南來幫助你設置應用程序。你可以按照指南瞭解基本用法,或者點擊 完成並完成 以跳過它。

請求訪問令牌

由於假設機器對機器應用程序是安全的(例如,它們部署在私有網絡中),因此我們可以使用 OAuth 2.0 "client_credentials" 授權來獲得訪問令牌。它使用基本身份驗證來驗證客戶端:

  • 請求 URL 是你的 Logto 實例的令牌端點。你可以在機器對機器應用程序詳細信息頁面的 高級設置 標籤中找到並複製它。
  • 請求方法是 POST
  • 請求 Content-Type 標頭是 application/x-www-form-urlencoded
  • 對於 Authorization 標頭,值是 Basic <base64(app_id:app_secret)>,其中 app_idapp_secret 是機器對機器應用程序的應用程序 ID 和應用程序密鑰。你可以在應用程序詳細信息頁面中找到它們。
  • 請求主體需要指定授權類型和 API 標識符。例如,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,我們可以發送以下請求:

驗證 JWT

你可能注意到支付服務需要使用 JWK 集驗證 JWT,並可能擁有本地 JWK 集緩存以避免每次都從 Logto 取取 JWK 集。幸運的是,由於 JWT 的普及,有很多庫可以幫助你用幾行代碼實現這個目標。

這些庫通常稱為 "jose" 或 "jsonwebtoken"。例如,在 Node.js 中,我們可以使用 jose 驗證 JWT:

如果驗證成功,payload 變量將是已解碼的 JWT 主體。否則,將拋出錯誤。

應用基於角色的訪問控制

現在,我們已經成功保護了購物車服務和支付服務之間的通信。然而,驗證流程僅能確保客戶端是真正的購物車服務,並不能確保購物車服務有任何權限在支付服務上執行動作。

比如說,我們想允許購物車服務創建支付,但不允許它讀取支付記錄。

定義權限

在 Logto 中,"範圍" 和 "權限" 是可以互換的。在支付服務的 API 資源詳細信息頁面上,導航到 權限 標籤頁。現在應該是空的。點擊 創建權限,輸入 read:payment 作為權限名稱,並輸入 讀取支付記錄 作為權限描述。然後點擊 創建權限

重複上述步驟以創建另一個名為 write:payment 並描述為 創建支付記錄 的權限。

創建機器對機器角色

角色是一組權限組合。在 Logto 中,機器對機器應用程序可以被指派角色以授予權限。在左側邊欄中點擊 "角色",然後點擊 創建角色

  1. 輸入 checkout 作為角色名稱,並輸入 結算服務 作為角色描述。
  2. 點擊 顯示更多選項。選擇 "機器對機器應用程序角色" 作為角色類型。
  3. 在 "分配的權限" 區域中,點擊 API 資源名稱(支付服務)左邊的箭頭圖標,然後選擇 write:payment 權限。
    創建角色
  4. 點擊 創建角色
  5. 由於我們已經有一個機器對機器應用程序(購物車服務),我們可以直接在下一步中分配角色給它。勾選應用程序名稱(購物車服務)左邊的復選框,然後點擊 分配應用程序
將角色分配給應用程序

請求帶有範圍的訪問令牌

除了我們在請求訪問令牌中提到的請求主體參數外,我們還可以在請求主體中指定範圍。例如,如果我們想請求 write:payment 權限,我們可以發送以下請求:

要請求多個範圍,你可以用空格分隔它們。例如,scope=write:payment read:payment

驗證範圍

如果需要支付服務中的某個動作需要 write:payment 權限,我們可以通過斷言 JWT 主體的 scope 聲明來驗證範圍:

結論

如果你想保護對購物車服務的訪問,你也可以應用相同的驗證流程。這一次,購物車服務是 API 資源,而客戶端是需要訪問的另一個服務。

使用 Logto,你的 API 資源通過 OAuth 2.0 和 JWT 得到保護,並且你可以通過應用基於角色的訪問控制來遵循最低權限原則。此外,你也可以使用 Logto 管理你的用戶及其權限,甚至與第三方身份提供者集成。