jwt-auth外掛程式實現了基於JWT(JSON Web Tokens)進行認證鑒權的功能,支援從HTTP請求的URL參數、要求標頭、Cookie欄位解析JWT,同時驗證該Token是否有許可權訪問。相較於配置JWT認證鑒權,jwt-auth外掛程式額外提供了調用方身份識別的能力,支援對不同調用方配置不同的JWT憑證。本文介紹如何配置jwt-auth外掛程式。
外掛程式類型
認證鑒權。
Token大小限制
HTTP協議對要求標頭的大小有限制。通常情況下,所有Header的總大小不超過60KB,具體取決於伺服器和用戶端的配置。如果 JWT Token 的大小超過該限制,可能導致請求失敗。
請確保 JWT Token 的總大小不超過伺服器要求標頭的最大限制,以避免請求失敗。
配置欄位
認證配置
名稱 | 資料類型 | 填寫要求 | 預設值 | 描述 |
consumers | array of object | 必填。 | - | 佈建服務的調用者,用於對請求進行認證。 |
global_auth | array of string | 選填(**僅執行個體層級配置**) | - | 只能在執行個體層級配置,若配置為true,則全域生效認證機制; 若配置為false,則只對做了配置的網域名稱和路由生效認證機制,若不配置則僅當沒有網域名稱和路由配置時全域生效(相容老使用者使用習慣)。 |
子項consumers中的每一項配置欄位說明如下。
名稱 | 資料類型 | 填寫要求 | 預設值 | 描述 |
name | string | 必填。 | - | 配置該consumer的名稱。 |
jwks | string | 選填。jwks和remote_jwks必填一個。 | - | JSON Web Key (JWK)指定的JSON格式字串,是由驗證JWT中籤名的公開金鑰(或對稱金鑰)組成的JSON Web Key Set。 |
remote_jwks | object | 選填。jwks和remote_jwks二選一。 | {"uri":"http://127.0.0.1/keys","service":"test.static","port":"80","ttl":30000,"timeout":3000} | 用於從指定服務的指定URI定時拉取JWKS,和JWKS欄位同時配置時,以此欄位配置拉取的JWKS優先。 |
issuer | string | 選填。 | - | JWT的簽發者,用於校正是否和payload中的iss欄位一致。 |
claims | object | 選填。 | - | key/value分別對應payload中的key/value,可以用於校正多個欄位是否和payload中一致,例如配置aud: mobile-site,則要求payload中的aud必須是mobile-site。 |
claims_to_headers | array of object | 選填。 | - | 抽取JWT的payload中指定欄位,設定到指定的要求標頭中,然後轉寄給後端。 |
from_headers | array of object | 選填。 | [{"name":"Authorization","value_prefix":"Bearer"}] | 從指定的要求標頭中抽取JWT。 |
from_params | array of string | 選填。 | access_token | 從指定的URL參數中抽取JWT。 |
from_cookies | array of string | 選填。 | - | 從指定的cookie中抽取JWT。 |
clock_skew_seconds | number | 選填。 | 60 | 校正JWT的exp和iat欄位時允許的時鐘位移量,單位為秒。 |
keep_token | bool | 選填。 | true | 轉寄給後端時是否保留JWT。 |
只有當from_headers,from_params,from_cookies均未配置時,才會使用預設值。
from_headers中每一項的配置欄位說明如下。名稱
資料類型
填寫要求
預設值
描述
name
string
必填。
-
抽取JWT的請求Header。
value_prefix
string
必填。
-
對請求Header的value去除此首碼,剩餘部分作為JWT。
claims_to_headers中每一項的配置欄位說明如下。名稱
資料類型
填寫要求
預設值
描述
claim
string
必填。
-
JWT payload中的指定欄位,要求必須是字串或不帶正負號的整數類型。
header
string
必填。
-
從payload取出欄位的值設定到這個要求標頭中,轉寄給後端。
override
bool
選填。
true
true時,存在同名要求標頭會進行覆蓋。
false時,追加同名要求標頭。
remote_jwks中每一項的配置欄位說明如下。名稱
資料類型
填寫要求
預設值
描述
uri
string
必填。
-
請求URL。
service
string
必填。
-
K8s服務填寫樣本:foo.default.svc.cluster.local。
Nacos服務填寫樣本:foo.DEFAULT-GROUP.public.nacos。
如果是名為test的DNS服務,則填寫test.dns。
如果是名為test的靜態IP服務,則填寫test.static。
port
bool
必填。
-
服務連接埠
timeout
bool
選填。
3000
服務要求逾時時間,單位是毫秒
ttl
bool
選填。
30000
緩衝時間,單位是毫秒
鑒權配置(非必需)
名稱 | 資料類型 | 填寫要求 | 預設值 | 描述 |
allow | array of string | 選填(**非執行個體層級配置**)。 | - | 只能在路由或網域名稱等細粒度規則上配置,對於符合匹配條件的請求,配置允許訪問的 consumer,從而實現細粒度的許可權控制。 |
在一個規則裡,鑒權配置和認證配置不可同時存在。
對於通過認證鑒權的請求,請求的Header會被添加一個
X-Mse-Consumer欄位,用以標識調用者的名稱。
配置樣本
全域配置認證和路由粒度進行鑒權
以下配置將對網關特定路由或網域名稱開啟JWT Auth認證和鑒權。
如果一個JWT能匹配多個jwks,則按照配置順序命中第一個匹配的consumer。
在執行個體層級做如下外掛程式配置:
consumers:
- name: consumer1
issuer: abcd
jwks: |
{
"keys": [
{
"kty": "oct",
"kid": "123",
"k": "hM0k3AbXBPpKOGg__Ql2Obcq7s60myWDpbHXzgKUQdYo7YCRp0gUqkCnbGSvZ2rGEl4YFkKqIqW7mTHdj-bcqXpNr-NOznEyMpVPOIlqG_NWVC3dydBgcsIZIdD-MR2AQceEaxriPA_VmiUCwfwL2Bhs6_i7eolXoY11EapLQtutz0BV6ZxQQ4dYUmct--7PLNb4BWJyQeWu0QfbIthnvhYllyl2dgeLTEJT58wzFz5HeNMNz8ohY5K0XaKAe5cepryqoXLhA-V-O1OjSG8lCNdKS09OY6O0fkyweKEtuDfien5tHHSsHXoAxYEHPFcSRL4bFPLZ0orTt1_4zpyfew",
"alg": "HS256"
}
]
}
- name: consumer2
issuer: abc
jwks: |
{
"keys": [
{
"kty": "RSA",
"e": "AQAB",
"use": "sig",
"kid": "123",
"alg": "RS256",
"n": "i0B67f1jggT9QJlZ_8QL9QQ56LfurrqDhpuu8BxtVcfxrYmaXaCtqTn7OfCuca7cGHdrJIjq99rz890NmYFZuvhaZ-LMt2iyiSb9LZJAeJmHf7ecguXS_-4x3hvbsrgUDi9tlg7xxbqGYcrco3anmalAFxsbswtu2PAXLtTnUo6aYwZsWA6ksq4FL3-anPNL5oZUgIp3HGyhhLTLdlQcC83jzxbguOim-0OEz-N4fniTYRivK7MlibHKrJfO3xa_6whBS07HW4Ydc37ZN3Rx9Ov3ZyV0idFblU519nUdqp_inXj1eEpynlxH60Ys_aTU2POGZh_25KXGdF_ZC_MSRw"
}
]
}在route-a和route-b兩個路由做如下外掛程式配置:
allow:
- consumer1在*.example.com和test.com兩個網域名稱做如下外掛程式配置:
allow:
- consumer2此例指定的
route-a和route-b即在建立網關路由時填寫的路由名稱,當匹配到這兩個路由時,將允許name為consumer1的調用者訪問,其他調用者不被允許訪問。此例指定的
*.example.com和test.com用於匹配請求的網域名稱,當發現網域名稱匹配時,將允許name為consumer2的調用者訪問,其他調用者不被允許訪問。
根據該配置,允許訪問以下請求。假設以下請求會匹配到route-a這條路由。
將JWT設定在URL參數中。
curl 'http://xxx.hello.com/test?access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEy****.eyJpc3MiOiJhYmNkIiwic3ViIjoidGVzdCIsImlhdCI6MTY2NTY2MDUyNywiZXhwIjoxODY1NjczOD****.-vBSV0bKeDwQcuS6eeSZN9dLTUnSnZVk8eVCXdooCQ4'將JWT設定在HTTP要求標頭中。
curl http://xxx.hello.com/test -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJpc3MiOiJhYmNkIiwic3ViIjoidGVzdCIsImlhdCI6MTY2NTY2MDUyNywiZXhwIjoxODY1NjczODE5fQ.-vBSV0bKeDwQcuS6eeSZN9dLTUnSnZVk8eVCXdooCQ4'
認證鑒權通過後,請求的Header中會被添加一個X-Mse-Consumer欄位,在此例中其值為consumer1,用以標識調用方的名稱。
下列請求將拒絕訪問。
請求未提供JWT,返回401。
curl http://xxx.hello.com/test根據請求提供的JWT匹配到的調用者無存取權限,返回403。
# consumer1不在*.example.com的allow列表裡 curl 'http://xxx.example.com/test' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJpc3MiOiJhYmNkIiwic3ViIjoidGVzdCIsImlhdCI6MTY2NTY2MDUyNywiZXhwIjoxODY1NjczODE5fQ.-vBSV0bKeDwQcuS6eeSZN9dLTUnSnZVk8eVCXdooCQ4'
網關執行個體層級開啟,但特定路由關閉
在執行個體層級做如下外掛程式配置:
global_auth: true
consumers:
- name: consumer1
issuer: abcd
jwks: |
{
"keys": [
{
"kty": "oct",
"kid": "123",
"k": "hM0k3AbXBPpKOGg__Ql2Obcq7s60myWDpbHXzgKUQdYo7YCRp0gUqkCnbGSvZ2rGEl4YFkKqIqW7mTHdj-bcqXpNr-NOznEyMpVPOIlqG_NWVC3dydBgcsIZIdD-MR2AQceEaxriPA_VmiUCwfwL2Bhs6_i7eolXoY11EapLQtutz0BV6ZxQQ4dYUmct--7PLNb4BWJyQeWu0QfbIthnvhYllyl2dgeLTEJT58wzFz5HeNMNz8ohY5K0XaKAe5cepryqoXLhA-V-O1OjSG8lCNdKS09OY6O0fkyweKEtuDfien5tHHSsHXoAxYEHPFcSRL4bFPLZ0orTt1_4zpyfew",
"alg": "HS256"
}
]
}
- name: consumer2
issuer: abc
jwks: |
{
"keys": [
{
"kty": "RSA",
"e": "AQAB",
"use": "sig",
"kid": "123",
"alg": "RS256",
"n": "i0B67f1jggT9QJlZ_8QL9QQ56LfurrqDhpuu8BxtVcfxrYmaXaCtqTn7OfCuca7cGHdrJIjq99rz890NmYFZuvhaZ-LMt2iyiSb9LZJAeJmHf7ecguXS_-4x3hvbsrgUDi9tlg7xxbqGYcrco3anmalAFxsbswtu2PAXLtTnUo6aYwZsWA6ksq4FL3-anPNL5oZUgIp3HGyhhLTLdlQcC83jzxbguOim-0OEz-N4fniTYRivK7MlibHKrJfO3xa_6whBS07HW4Ydc37ZN3Rx9Ov3ZyV0idFblU519nUdqp_inXj1eEpynlxH60Ys_aTU2POGZh_25KXGdF_ZC_MSRw"
}
]
}在route-b路由做如下外掛程式配置:
_disable_: true此例route-b即在建立網關路由時填寫的路由名稱。因為_disable_設定為true,當匹配到這個路由時,將關閉此外掛程式功能,不進行JWT認證,所有人均可訪問。
當沒有匹配到route-b路由時,因為全域配置中global_auth設定為了true,表明對所有請求開啟認證,所以將進行JWT認證,並且允許consumer1和consumer2都能訪問。
網域名稱層級開啟,但特定路由關閉
在執行個體層級做如下外掛程式配置:
consumers:
- name: consumer1
issuer: abcd
jwks: |
{
"keys": [
{
"kty": "oct",
"kid": "123",
"k": "hM0k3AbXBPpKOGg__Ql2Obcq7s60myWDpbHXzgKUQdYo7YCRp0gUqkCnbGSvZ2rGEl4YFkKqIqW7mTHdj-bcqXpNr-NOznEyMpVPOIlqG_NWVC3dydBgcsIZIdD-MR2AQceEaxriPA_VmiUCwfwL2Bhs6_i7eolXoY11EapLQtutz0BV6ZxQQ4dYUmct--7PLNb4BWJyQeWu0QfbIthnvhYllyl2dgeLTEJT58wzFz5HeNMNz8ohY5K0XaKAe5cepryqoXLhA-V-O1OjSG8lCNdKS09OY6O0fkyweKEtuDfien5tHHSsHXoAxYEHPFcSRL4bFPLZ0orTt1_4zpyfew",
"alg": "HS256"
}
]
}
- name: consumer2
issuer: abc
jwks: |
{
"keys": [
{
"kty": "RSA",
"e": "AQAB",
"use": "sig",
"kid": "123",
"alg": "RS256",
"n": "i0B67f1jggT9QJlZ_8QL9QQ56LfurrqDhpuu8BxtVcfxrYmaXaCtqTn7OfCuca7cGHdrJIjq99rz890NmYFZuvhaZ-LMt2iyiSb9LZJAeJmHf7ecguXS_-4x3hvbsrgUDi9tlg7xxbqGYcrco3anmalAFxsbswtu2PAXLtTnUo6aYwZsWA6ksq4FL3-anPNL5oZUgIp3HGyhhLTLdlQcC83jzxbguOim-0OEz-N4fniTYRivK7MlibHKrJfO3xa_6whBS07HW4Ydc37ZN3Rx9Ov3ZyV0idFblU519nUdqp_inXj1eEpynlxH60Ys_aTU2POGZh_25KXGdF_ZC_MSRw"
}
]
}在route-b路由做如下外掛程式配置:
_disable_: true在*.example.com網域名稱做如下外掛程式配置:
allow:
- consumer1
- consumer2此例中route-b即在建立網關路由時填寫的路由名稱。因為_disable_設定為true,當匹配到這個路由時,將關閉此外掛程式功能,不進行JWT認證,所有人均可訪問。
此例中指定的*.example.com用於匹配請求的網域名稱。當發現網域名稱匹配時,將允許name為consumer1或consumer2的調用者訪問,其他調用者不允許訪問。
配置規則順序會依次執行匹配,有任一規則匹配到,就會執行對應配置,不再匹配其他規則。因為這裡配置路由匹配優先順序高於網域名稱,即對當前配置的網域名稱生效,而對網域名稱下的特定路由關閉。
相關錯誤碼
HTTP狀態代碼 | 出錯資訊 | 原因說明 |
401 | Jwt missing | 要求標頭未提供JWT。 |
401 | Jwt expired | JWT已經到期。 |
401 | Jwt verification fails | JWT payload校正失敗,如iss不匹配。 |
403 | Access Denied | 無許可權訪問當前路由。 |