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
支援不含
kid
的jwk
支援配置多個
jwk
支援直接從
header
或query
讀取Token,不需要每個API都設定Token參數支援從請求的cookie頭的欄位中讀取Token
當
JWT
以Authorization 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認證外掛程式支援以下演算法:
簽名演算法 | 支援的 |
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認證外掛程式
會使用配置的parameter
與parameterLocation
來讀取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. 校正規則
外掛程式會通過配置的
paramater
與parameterToken
來擷取到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. 配置案例
4.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
4.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...
4.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
4.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....
5. 相關錯誤碼
Status | Code | Message | Description |
400 | I400JR | JWT required | 未找到JWT參數 |
403 | S403JI | Claim | 當在 |
403 | S403JU | Claim | 當在 |
403 | A403JT | Invalid JWT: ${Reason} | 請求中提供的 |
400 | I400JD | JWT Deserialize Failed: | 請求中提供的 |
403 | A403JK | No matching JWK, | 請求 |
403 | A403JE | JWT is expired at | 請求中提供的 |
400 | I400JP | Invalid JWT plugin config: ${JWT} |
|
當出現非預期應答碼時,請檢查HTTP應答中的X-Ca-Error-Code
頭中擷取ErrorCode
,從X-Ca-Error-Message
頭中擷取ErrorMessage
當出現A403JT
或I400JD
錯誤碼時,可訪問jwt.io網站來檢查自己的Token合法性與格式。
6. 使用限制
外掛程式中繼資料的大小限制為50KB。
轉寄參數列表最大限制為16個,
claimName
與parameterName
長度不超過32個字元, 僅支援[A-Za-z0-9-_]。JWK的
alg
支援列表為:RS256, RS384, RS512, ES256, ES384, ES512, HS256, HS384, HS512。