全部產品
Search
文件中心

API Gateway:JWT認證外掛程式

更新時間:Dec 18, 2024

Json Web Token(RFC7519), 是一種便捷的可用於網關進行請求認證的鑒權方案,API Gateway可以通過託管使用者的Public JWK實現對請求進行JWT認證,並將claims作為後端參數轉寄給後端,以方便使用者簡化開發後端應用的開發。本文主要是JWT認證外掛程式的配置,如果需要瞭解JWT的token認證流程及基礎知識可以參考基於JWT的token認證

原有在API上配置的OpenId Connect功能目前可以通過JWT認證外掛程式實現,推薦使用JWT認證外掛程式配置,相比配置在API上的OpenId Connect配置,JWT認證外掛程式具備以下優勢:

  • 不再需要額外配置一個授權API,可以用任何方式來產生與分發JWT,API Gateway僅負責通過公開金鑰JWK認證JWT

  • 支援不含kidjwk

  • 支援配置多個jwk

  • 支援直接從headerquery讀取Token,不需要每個API都設定Token參數

  • 支援從請求的cookie頭的欄位中讀取Token

  • JWTAuthorization bearer {token}方式傳輸時,可以通過parameter: Authorization, parameterLocation: header方式配置已實現正確的Token讀取

  • 通過添加preventJtiReplay: true配置,可支援基於claim:jti的防重放檢查

  • 通過添加bypassEmptyToken: true配置,可在Token不存在時跳過驗證直接轉寄給後端

  • 通過添加ignoreExpirationCheck: true配置,可忽略Token的exp超期校正

如果你配置了JWT認證外掛程式並綁定到了已配置了OpenId Connect功能的API上,原有API上的OpenId Connect的功能將被外掛程式的配置覆蓋。

1. 擷取JWK (Json Web Key)

JWT認證外掛程式通過Json Web Key(RFC7517),實現JWT的簽名與認證,配置JWT認證外掛程式首先需要產生一個有效Json Web Key,您可以通過自行產生,或搜尋Json Web Key Generator尋找可用的線上產生工具,如mkjwk.org,一個可用的Json Web Key大概如下所示,其中私密金鑰用於對Token進行簽名,公開金鑰需要配置在JWT認證外掛程式中用於對Token進行簽名,一個合法的JWK大概格式如下:

{
  "kty": "RSA",
  "e": "AQAB",
  "kid": "O9fpdhrViq2zaaaBEWZITz",
  "use": "sig",
  "alg": "RS256",
  "n": "qSVxcknOm0uCq5vGsOmaorPDzHUubBmZZ4UXj-9do7w9X1uKFXAnqfto4TepSNuYU2bA_-tzSLAGBsR-BqvT6w9SjxakeiyQpVmexxnDw5WZwpWenUAcYrfSPEoNU-0hAQwFYgqZwJQMN8ptxkd0170PFauwACOx4Hfr-9FPGy8NCoIO4MfLXzJ3mJ7xqgIZp3NIOGXz-GIAbCf13ii7kSStpYqN3L_zzpvXUAos1FJ9IPXRV84tIZpFVh2lmRh0h8ImK-vI42dwlD_hOIzayL1Xno2R0T-d5AwTSdnep7g-Fwu8-sj4cCRWq3bd61Zs2QOJ8iustH0vSRMYdP5oYQ"
}        

這裡展示的是JSON格式,當使用YAML格式配置外掛程式,需要轉換*

  • JWT認證外掛程式只需要配置Public Key, 請妥善儲存好您的Private Key,目前JWT認證外掛程式支援以下演算法:

簽名演算法

支援的alg取值

RSASSA-PKCS1-V1_5 with SHA-2

RS256, RS384, RS512

Elliptic Curve (ECDSA) with SHA-2

ES256, ES384, ES512

HMAC using SHA-2

HS256, HS384, HS512

重要

當配置HS256,HS384,HS512類型的Key時,密鑰需要為Base64 UrlEncode後的值,如遇到Invalid Signature問題,請檢查您的Key的格式是否與產生Token的Key一致

2. 外掛程式配置

您可以選擇JSON或者YAML格式的來配置您的外掛程式,兩種格式的schema相同,請搜尋yaml to json轉換工具來進行配置格式的轉換,YAML格式的模板見下表。

---
parameter: X-Token           # 從指定的參數中擷取JWT, 對應API的參數 
parameterLocation: header    # API為映射模式時可選, API為透傳模式下必填, 用於指定JWT的讀取位置, 僅支援`query`,`header`
preventJtiReplay: false      # 是否開啟針對`jti`的防重放檢查, 預設: false
bypassEmptyToken: false      # 當`jwt`為空白時是否允許驗證通過
ignoreExpirationCheck: false # 是否允許忽略`exp`超期時間的檢查
orAppAuth: false             # 預設為false,阿里雲APP認證和JWT外掛程式都會校正;為true時,阿里雲APP認證通過後將不再校正JWT外掛程式,JWT外掛程式通過後將不再校正阿里雲APP認證
claimParameters:             # claims參數轉換, 網關會將jwt claims映射為後端參數
- claimName: aud             # claim名稱,支援公用和私人
  parameterName: X-Aud       # 映射後參數名稱
  location: header           # 映射後參數位置, 支援`query,header,path,formData`
- claimName: userId          # claim名稱,支援公用和私人
  parameterName: userId      # 映射後參數名稱
  location: query            # 映射後的參數位置, 支援`query,header,path,formData`
#
# `Json Web Key`的`Public Key`
jwk:
  kty: RSA
  e: AQAB
  use: sig
  alg: RS256
  n: qSVxcknOm0uCq5vGsOmaorPDzHUubBmZZ4UXj-9do7w9X1uKFXAnqfto4TepSNuYU2bA_-tzSLAGBsR-BqvT6w9SjxakeiyQpVmexxnDw5WZwpWenUAcYrfSPEoNU-0hAQwFYgqZwJQMN8ptxkd0170PFauwACOx4Hfr-9FPGy8NCoIO4MfLXzJ3mJ7xqgIZp3NIOGXz-GIAbCf13ii7kSStpYqN3L_zzpvXUAos1FJ9IPXRV84tIZpFVh2lmRh0h8ImK-vI42dwlD_hOIzayL1Xno2R0T-d5AwTSdnep7g-Fwu8-sj4cCRWq3bd61Zs2QOJ8iustH0vSRMYdP5oYQ
#
# 可以支援配置多個JWK, 可以與jwk欄位一起配置
# 配置多個JWK時,kid是必選的,如果JWT中沒有提供kid,則校正失敗
jwks:
- kid: O9fpdhrViq2zaaaBEWZITz         # 在只配置一個JWK時,kid是可選的,但如果中JWT包含了kid,網關會校正kid的一致性
  kty: RSA
  e: AQAB
  use: sig
  alg: RS256
  n: qSVxcknOm0uCq5v....
- kid: 10fpdhrViq2zaaaBEWZITz         # 在只配置一個JWK時,kid是可選的,但如果中JWT包含了kid,網關會校正kid的一致性
  kty: RSA
  e: AQAB
  use: sig
  alg: RS256
  n: qSVxcknOm0uCq5v...                
  • JWT認證外掛程式會使用配置的parameterparameterLocation來讀取JWT的值,如:parameter: X-Token, parameterLocation: header表示從請求的X-Token頭讀取JWT

  • 如果API上配置了與parameter配置同名的參數時,parameterLocation可以忽略,否則會在調用時報錯

  • 如果使用Authorization頭傳輸Token,如:Authorization bearer {token}, 可以通過parameter: Authorization, parameterLocation: header方式配置,JWT外掛程式可以正確讀取到Token的取值

  • 當配置了preventJtiReplay: true時,外掛程式會使用claims中的jti欄位進行防重放檢查

  • 當配置了bypassEmptyToken: true時,當Token參數為空白時,允許跳過檢查,直接轉寄請求給後端

  • 當配置了ignoreExpirationCheck: true時,會跳過exp的超期檢查,否則網關會檢查Token是否已超期

  • 如果需要API Gateway將Token中的claims轉寄給後端應用,可以通過tokenParameters欄位配置轉寄參數

    • claimName: token中claim的名字,當配置HS256,HS384,HS512類型的Key時,密鑰為Base64 UrlEncode後的值,如遇到Invalid Signature問題,請檢查您的Key的格式是否與產生Token的Key一致支援配置kid

    • parameterName:轉寄到後端的參數名稱

    • location:轉寄到後端的參數位置,支援header,query,path,formData

      • 當配置為path時,後端path必須包含同名的參數,如/path/{userId}

      • 當配置為formData時,僅支援在參數映射且包體為form格式下才能正確映射到後端的form中

  • 可以在jwk欄位中配置單個Key,或在jwks字中配置多個Key

    • 不包含kid欄位的Key只允許配置一個

    • 包含kid的Key可以配置多個,但kid必須唯一

3. 校正規則

  • 外掛程式會通過配置的paramaterparameterToken來擷取到Token, 如果希望在Token不存在時仍然轉寄請求給後端,需要配置bypassEmptyToken: true

  • 當配置多個Key是的選擇原則為

    • 優先選擇與請求Token中kid一致的Key來進行簽名校正

    • 不含kid的Key只能配置一個,當不存在kid與請求Token一致的情況時,使用不含kid的Key執行簽名校正

    • 如果沒有配置不含kid的Key,如果請求Token中未包含kid,或通過kid未匹配到Key則報錯A403JK

  • 如果Token中包含了iat, nbf, exp欄位,JWT外掛程式會校正時間欄位的合法性,時間單位是秒(s)。

  • 預設情況下,API Gateway會校正Token的exp到期時間欄位,如果希望跳過exp的超期檢查,需要配置ignoreExpirationCheck: true

  • 如果配置了tokenParameters欄位轉寄,當Token的claims中包含對應的欄位時,claim欄位會轉寄給後端,不存在的claim不會轉寄

4. JWT認證外掛程式支援外掛程式資料集

4.1 建立JWT認證外掛程式資料集

  1. 登入API Gateway控制台,在左側欄導覽列單擊API管理 > 外掛程式管理

  2. 在外掛程式列表頁面,選擇外掛程式資料集

  3. 單擊右上方的建立資料集,在彈出框中自訂資料集的名稱,類型選擇JWT_JWK_LIST,單擊確定即可產生資料集。

  4. 進入剛產生的資料集,單擊右上方的建立資料集條目,在資料值中配置JWT認證外掛程式支援的JWK,在到期時間中配置公開金鑰的有效期間。

    重要

    當配置多個JWK時,需要使用不同的kid,JWK資料值的順序要按照以下樣本順序配置。

    kty: RSA
    e: AQAB
    use: sig
    kid: N3h666
    alg: RS256
    n: qfzaxmlnl...

4.2 JWT認證外掛程式配置外掛程式資料集樣本

---
parameter: X-Token         # 從指定的參數中擷取JWT, 對應API的參數
parameterLocation: header  # API為映射模式時可選, API為透傳模式下必填, 用於指定JWT的讀取位置, 僅支援`query`,`header`
claimParameters:           # claims參數轉換, 網關會將jwt claims映射為後端參數
- claimName: aud           # claim名稱,支援公用和私人
  parameterName: X-Aud     # 映射後參數名稱
  location: header         # 映射後參數位置, 支援`query,header,path,formData`
- claimName: userId        # claim名稱,支援公用和私人
  parameterName: userId    # 映射後參數名稱
  location: query          # 映射後的參數位置, 支援`query,header,path,formData`
preventJtiReplay: false    # 是否開啟針對`jti`的防重放檢查, 預設: false
ignoreExpirationCheck: true  # 是否允許忽略`exp`超期時間的檢查
jwkListDataSet: cb4f000b6b8244329ac25XXXc8a4f9d6  # 外掛程式資料集ID
說明

如果不生效需提交工單升級執行個體版本。

5. 配置案例

5.1. 配置單個JWK

---
parameter: X-Token         # 從指定的參數中擷取JWT, 對應API的參數
parameterLocation: header  # API為映射模式時可選, API為透傳模式下必填, 用於指定JWT的讀取位置, 僅支援`query`,`header`
claimParameters:           # claims參數轉換, 網關會將jwt claims映射為後端參數
- claimName: aud           # claim名稱,支援公用和私人
  parameterName: X-Aud     # 映射後參數名稱
  location: header         # 映射後參數位置, 支援`query,header,path,formData`
- claimName: userId        # claim名稱,支援公用和私人
  parameterName: userId    # 映射後參數名稱
  location: query          # 映射後的參數位置, 支援`query,header,path,formData`
preventJtiReplay: false    # 是否開啟針對`jti`的防重放檢查, 預設: false
#
# `Json Web Key`的`Public Key`
jwk:
  kty: RSA
  e: AQAB
  use: sig
  alg: RS256
  n: qSVxcknOm0uCq5vGsOmaorPDzHUubBmZZ4UXj-9do7w9X1uKFXAnqfto4TepSNuYU2bA_-tzSLAGBsR-BqvT6w9SjxakeiyQpVmexxnDw5WZwpWenUAcYrfSPEoNU-0hAQwFYgqZwJQMN8ptxkd0170PFauwACOx4Hfr-9FPGy8NCoIO4MfLXzJ3mJ7xqgIZp3NIOGXz-GIAbCf13ii7kSStpYqN3L_zzpvXUAos1FJ9IPXRV84tIZpFVh2lmRh0h8ImK-vI42dwlD_hOIzayL1Xno2R0T-d5AwTSdnep7g-Fwu8-sj4cCRWq3bd61Zs2QOJ8iustH0vSRMYdP5oYQ
                        

5.2. 配置多個JWK

---
parameter: Authorization   # 從Authorization頭中擷取Token 
parameterLocation: header  # 從Authorization頭中擷取Token
claimParameters:           # claims參數轉換, 網關會將jwt claims映射為後端參數
- claimName: aud           # claim名稱,支援公用和私人
  parameterName: X-Aud     # 映射後參數名稱
  location: header         # 映射後參數位置, 支援`query,header,path,formData`
- claimName: userId        # claim名稱,支援公用和私人
  parameterName: userId    # 映射後參數名稱
  location: query          # 映射後的參數位置, 支援`query,header,path,formData`
preventJtiReplay: true     # 是否開啟針對`jti`的防重放檢查, 預設: false
jwks:
- kid: O9fpdhrViq2zaaaBEWZITz         # 配置多個JWK時,需要使用不同的`kid`
  kty: RSA
  e: AQAB
  use: sig
  alg: RS256
  n: qSVxcknOm0uCq5v....
- kid: 10fpdhrViq2zaaaBEWZITz         # 配置多個JWK時,需要使用不同的`kid`
  kty: RSA
  e: AQAB
  use: sig
  alg: RS256
  n: qSVxcknOm0uCq5v...

5.3. 從請求的Cookie中的欄位中讀取Token

---
parameter: cookie         # 從指定的參數中擷取JWT, 對應API的參數
parameterLocation: header  # API為映射模式時可選, API為透傳模式下必填, 用於指定JWT的讀取位置, 僅支援`query`,`header`
parameterSection: token    # 例如cookie的值為 username=tom ; token=abcsef
claimParameters:           # claims參數轉換, 網關會將jwt claims映射為後端參數
- claimName: aud           # claim名稱,支援公用和私人
  parameterName: X-Aud     # 映射後參數名稱
  location: header         # 映射後參數位置, 支援`query,header,path,formData`
- claimName: userId        # claim名稱,支援公用和私人
  parameterName: userId    # 映射後參數名稱
  location: query          # 映射後的參數位置, 支援`query,header,path,formData`
preventJtiReplay: true     # 是否開啟針對`jti`的防重放檢查, 預設: false
jwks:
- kid: O9fpdhrViq2zaaaBEWZITz         # 配置多個JWK時,需要使用不同的`kid`
  kty: RSA
  e: AQAB
  use: sig
  alg: RS256
  n: qSVxcknOm0uCq5v....
- kid: 10fpdhrViq2zaaaBEWZITz         # 配置多個JWK時,需要使用不同的`kid`
  kty: RSA
  e: AQAB
  use: sig
  alg: RS256
  n: qSVxcknOm0uCq5v...

有些Web情境,為了安全考慮,使用者想把Token放在Cookie的一個指定的欄位儲存,API Gateway的JWT外掛程式支援從請求的Cookie的欄位中讀取Token,使用parameterSection參數特性就可以指定欄位名稱了。比如請求的Cookie頭如下所示,API Gateway就可以把Cookie中的Token提取出來。

Cookie: acw_tc=123; token=0QzRCMDBBQUYwRjE1MjYxQzU0QjY4NEM5MTc1NTQ1OUVCOTIzNzA4RDk3MDg5MzlDOTMQTVENDZCRUI1NkYyMEUyO; csrf=073957d8d2823be4f6c0cad23c764558

5.4 配置黑名單

JWT認證外掛程式黑名單的使用情境是屏蔽已經獲得了正式Token,但被拉到黑名單的使用者請求。API Gateway的JWT認證外掛程式為這種情境結合外掛程式資料集的能力上線了根據Token中解密出來的claim參數進行拒絕請求的能力。API Gateway不僅能夠拒絕命中了Blocking條件的請求,還能自訂拒絕的應答。具體的配置方法如下,請注意所有以block開頭的參數定義:

---
parameter: Authorization   # 從Authorization頭中擷取Token 
parameterLocation: header  # 從Authorization頭中擷取Token
claimParameters:           # claims參數轉換, 網關會將jwt claims映射為後端參數
- claimName: aud           # claim名稱,支援公用和私人
  parameterName: X-Aud     # 映射後參數名稱
  location: header         # 映射後參數位置, 支援`query,header,path,formData`
- claimName: userId        # claim名稱,支援公用和私人
  parameterName: userId    # 映射後參數名稱
  location: query          # 映射後的參數位置, 支援`query,header,path,formData`
blockClaimParameterName: userId  # 映射後的參數位置, 支援`query,header,path,formData`
blockByDataSet: 87b65008e92541938537b1a4a236eda5  # 映射後的參數位置, 支援`query,header,path,formData`
blockStatusCode: 403       # 拒絕請求返回的應答StatusCode定義
blockResponseHeaders:      # 拒絕請求返回的應答頭定義
  Content-Type: application/xml
blockResponseBody:         # 拒絕請求返回的Body定義
  <Reason>be blocked</Reason>
jwks:
- kid: O9fpdhrViq2zaaaBEWZITz         # 配置多個JWK時,需要使用不同的`kid`
  kty: RSA
  e: AQAB
  use: sig
  alg: RS256
  n: qSVxcknOm0uCq5v....

6. 相關錯誤碼

Status

Code

Message

Description

400

I400JR

JWT required

未找到JWT參數

403

S403JI

Claim jti is required when preventJtiReplay:true

當在JWT授權外掛程式中配置了防重放功能時,請求未提供有效jti

403

S403JU

Claim jti in JWT is used

當在JWT授權外掛程式中配置了防重放功能時,請求提供的jti已被使用

403

A403JT

Invalid JWT: ${Reason}

請求中提供的JWT非法

400

I400JD

JWT Deserialize Failed: ${Token}

請求中提供的JWT解析失

403

A403JK

No matching JWK, kid:${kid} not found

請求JWT中的kid沒有匹配的JWK

403

A403JE

JWT is expired at ${Date}

請求中提供的JWT已到期

400

I400JP

Invalid JWT plugin config: ${JWT}

JWT授權外掛程式配置錯誤

當出現非預期應答碼時,請檢查HTTP應答中的X-Ca-Error-Code頭中擷取ErrorCode,從X-Ca-Error-Message頭中擷取ErrorMessage當出現A403JTI400JD錯誤碼時,可訪問jwt.io網站來檢查自己的Token合法性與格式。

7. 使用限制

  • 外掛程式中繼資料的大小限制為50KB。

  • 轉寄參數列表最大限制為16個,claimNameparameterName長度不超過32個字元,僅支援[A-Za-z0-9-_]。

  • JWK的alg支援列表為:RS256, RS384, RS512, ES256, ES384, ES512, HS256, HS384, HS512。