Json Web Token(RFC7519), 是一種便捷的可用于網關進行請求認證的鑒權方案,API 網關
可以通過托管用戶的Public JWK
實現對請求進行JWT認證,并將claims
作為后端參數轉發給后端,以方便用戶簡化開發后端應用的開發。本文主要是JWT認證插件的配置,如果需要了解JWT的token認證流程及基礎知識可以參考基于JWT的token認證。
原有在API上配置的OpenId Connect
功能目前可以通過JWT認證插件
實現,推薦使用JWT認證
插件配置,相比配置在API上的OpenId Connect
配置,JWT認證
插件具備以下優勢:
不再需要額外配置一個
授權API
,可以用任何方式來生成與分發JWT
,API網關僅負責通過公鑰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網關將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網關會校驗Token的
exp
過期時間字段,如果希望跳過exp
的超期檢查,需要配置ignoreExpirationCheck: true
如果配置了
tokenParameters
字段轉發,當Token的claims
中包含對應的字段時,claim
字段會轉發給后端,不存在的claim
不會轉發
4. JWT認證插件支持插件數據集
4.1 創建JWT認證插件數據集
登錄API網關控制臺,在左側欄導航欄單擊API管理 > 插件管理 。
在插件列表頁面,選擇插件數據集。
單擊右上角的創建數據集,在彈出框中自定義數據集的名稱,類型選擇JWT_JWK_LIST,單擊確定即可生成數據集。
進入剛生成的數據集,單擊右上角的創建數據集條目,在數據值中配置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網關的JWT插件支持從請求的Cookie的字段中讀取Token,使用parameterSection參數特性就可以指定字段名稱了。比如請求的Cookie頭如下所示,API網關就可以把Cookie中的Token提取出來。
Cookie: acw_tc=123; token=0QzRCMDBBQUYwRjE1MjYxQzU0QjY4NEM5MTc1NTQ1OUVCOTIzNzA4RDk3MDg5MzlDOTMQTVENDZCRUI1NkYyMEUyO; csrf=073957d8d2823be4f6c0cad23c764558
5.4 配置黑名單
JWT認證插件黑名單的使用場景是屏蔽已經獲得了正式Token,但被拉到黑名單的用戶請求。API網關的JWT認證插件為這種場景結合插件數據集的能力上線了根據Token中解密出來的claim參數進行拒絕請求的能力。API網關不僅能夠拒絕命中了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 | 當在 |
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合法性與格式。
7. 使用限制
插件元數據的大小限制為50KB。
轉發參數列表最大限制為16個,
claimName
與parameterName
長度不超過32個字符,僅支持[A-Za-z0-9-_]。JWK的
alg
支持列表為:RS256, RS384, RS512, ES256, ES384, ES512, HS256, HS384, HS512。