jwt-verification: 用于 nginx-module-lua 的 JWT 验证库,支持 JWKS 集成
安装
如果您尚未设置 RPM 仓库订阅,请 注册。然后您可以继续以下步骤。
CentOS/RHEL 7 或 Amazon Linux 2
yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install lua-resty-jwt-verification
CentOS/RHEL 8+、Fedora Linux、Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install lua5.1-resty-jwt-verification
要在 NGINX 中使用此 Lua 库,请确保已安装 nginx-module-lua。
本文档描述了 lua-resty-jwt-verification v0.7.0,发布于 2025 年 10 月 30 日。
OpenResty 的 JWT 验证库。
描述
OpenResty 的 JWT 验证库。
该项目的目标是成为 lua-resty-jwt 的现代和更轻量的替代品,内置支持 JWKS。
该项目不提供 JWT 操作或创建功能:您只能验证/解密令牌。
库的非目标
- JWT 创建/修改
- 为了 RFC 的完整性而功能完整。
- 不会实现无意义和不安全的 RFC 特性(例如,alg none)。
与 lua-resty-jwt 的区别
主要区别如下: - 不支持任何形式的 JWT 操作(您只能解密/验证它们) - 更简单的内部结构,依赖于更新的 lua-resty-openssl 和 OpenSSL 版本。 - 支持不同的 JWE 算法(见上面的表格)。 - 自动 JWT 验证,给定 JWKS HTTP 端点。
如果上述任何一点是个问题,或者您需要与旧版 OpenResty 兼容,我建议您坚持使用 lua-resty-jwt。
类型
类型和空检查通过广泛使用 EmmyLua 注释 提供。
EmmyLua 的 IDE 插件集成: - Idea - VSCode
项目根目录中的文件 ngx.d.lua 提供了一些 ngx 存根。
支持的功能
- JWS 验证:使用对称或非对称密钥。
- JWE 解密:使用对称或非对称密钥。
- 支持的非对称密钥格式:
- PEM
- DER
- JWK
- JWT 声明验证。
- 自动 JWKS 获取和 JWT 验证。
- 可选的缓存策略。
- 嵌套 JWT(JWS 在 JWE 中)
JWS 验证
| 声明 | 已实现 |
|---|---|
| alg | |
| jku | |
| jwk | |
| kid | |
| x5u | |
| x5c | |
| x5t | |
| x5t#S256 | |
| typ | |
| cty | |
| crit |
| alg | 已实现 | JOSE 实现要求 | 要求 |
|---|---|---|---|
| HS256 | 必需 | ||
| HS384 | 可选 | ||
| HS512 | 可选 | ||
| RS256 | 推荐 | ||
| RS384 | 可选 | ||
| RS512 | 可选 | ||
| ES256 | 推荐+ | ||
| ES384 | 可选 | ||
| ES512 | 可选 | ||
| PS256 | 可选 | ||
| PS384 | 可选 | ||
| PS512 | 可选 | ||
| none | 可选 | ||
| EdDSA | 已弃用 | ||
| ES256K | 可选 | ||
| Ed25519 | 可选 | *OpenSSL 3.0+ | |
| Ed448 | 可选 |
JWE 解密
| 声明 | 已实现 |
|---|---|
| alg | |
| enc | |
| zip | |
| jku | |
| jwk | |
| kid | |
| x5u | |
| x5c | |
| x5t | |
| x5t#S256 | |
| typ | |
| cty | |
| crit |
| kty | 已实现 | JOSE 实现要求 |
|---|---|---|
| EC | 推荐+ | |
| RSA | 必需 | |
| oct | 必需 | |
| OKP | 可选 |
| alg | 已实现 | JOSE 实现要求 | 要求 |
|---|---|---|---|
| RSA1_5 | 推荐- | ||
| RSA-OAEP | 推荐+ | ||
| RSA-OAEP-256 | 可选 | ||
| RSA-OAEP-384 | 可选 | ||
| RSA-OAEP-512 | 可选 | ||
| A128KW | 推荐 | *OpenSSL 3.0+ | |
| A192KW | 可选 | *OpenSSL 3.0+ | |
| A256KW | 推荐 | *OpenSSL 3.0+ | |
| dir | 推荐 | ||
| ECDH-ES | 推荐+ | ||
| ECDH-ES+A128KW | 推荐 | *OpenSSL 3.0+ | |
| ECDH-ES+A192KW | 可选 | *OpenSSL 3.0+ | |
| ECDH-ES+A256KW | 推荐 | *OpenSSL 3.0+ | |
| A128GCMKW | 可选 | ||
| A192GCMKW | 可选 | ||
| A256GCMKW | 可选 | ||
| PBES2-HS256+A128KW | 可选 | ||
| PBES2-HS384+A192KW | 可选 | ||
| PBES2-HS512+A256KW | 可选 |
*包含 OpenSSL 3.0+ 的 OpenResty 的第一个正式版本是 OpenResty 1.27.1.1, 该版本附带 OpenSSL 3.0.15(是的,那个 非常慢的 OpenSSL 3.0 系列...)。
所以,请使用 OpenResty 1.27.1.2 作为最低要求,该版本附带 OpenSSL 3.4.1。
| enc | 已实现 | JOSE 实现要求 |
|---|---|---|
| A128CBC-HS256 | 必需 | |
| A192CBC-HS384 | 可选 | |
| A256CBC-HS512 | 必需 | |
| A128GCM | 推荐 | |
| A192GCM | 可选 | |
| A256GCM | 推荐 |
JWKS 检索缓存策略
| 缓存策略 | 已实现 |
|---|---|
| 无缓存 | |
| 本地 (shared_dict) |
JWT 验证用法
jwt.decode_header_unsafe
语法: header, err = jwt.decode_header_unsafe(token)
读取 jwt 头并将其转换为 lua 表。
重要: 此方法不验证 JWT 签名!仅在您需要检查令牌的头而不进行完整验证时使用。
local jwt = require("resty.jwt-verification")
local token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE3MTY2NDkwNzJ9._MwFdsBPSyci9iARpoAaulReGcn1q7mKiPZjR2JDvdY"
local header, err = jwt.decode_header_unsafe(token)
if not header then
return nil, "malformed jwt: " .. err
end
print("alg: " .. header.alg) -- alg: HS256
jwt.verify
语法: decoded_token, err = jwt.verify(token, secret, options?)
验证 JWS 令牌并将其转换为 lua 表。
可选参数 options 可以传递以配置令牌验证器。有效字段包括:
- valid_signing_algorithms (dictalg 声明的字典。
- typ (string | nil): 如果不为 null,确保 JWT 声明 typ 匹配传递的值。
- issuer (string | nil): 如果不为 null,确保 JWT 声明 iss 匹配传递的值。
- audiences (string | table | nil): 如果不为 null,确保 JWT 声明 aud 匹配提供的值之一。
- subject (string | nil): 如果不为 null,确保 JWT 声明 sub 匹配传递的值。
- jwtid (string | nil): 如果不为 null,确保 JWT 声明 jti 匹配传递的值。
- ignore_not_before (bool): 如果为 true,则将忽略 JWT 声明 nbf。
- ignore_expiration (bool): 如果为 true,则将忽略 JWT 声明 exp。
- current_unix_timestamp (datetime | nil): JWT nbf 和 exp 声明将根据此时间戳进行验证。如果为 null,
将使用 ngx.time() 提供的当前日期时间。
- timestamp_skew_seconds (int): 库可以使用多少秒的宽限时间来检查令牌过期与当前时间的关系。当时钟并不总是完全同步时很有用。将此值设置得过高可能会带来安全问题。
options 字段的默认值:
local verify_default_options = {
valid_signing_algorithms = {
["HS256"]="HS256", ["HS384"]="HS384", ["HS512"]="HS512",
["RS256"]="RS256", ["RS384"]="RS384", ["RS512"]="RS512",
["ES256"]="ES256", ["ES384"]="ES384", ["ES512"]="ES512",
["PS256"]="PS256", ["PS384"]="PS384", ["PS512"]="PS512",
["ES256K"]="ES256K", ["Ed25519"]="Ed25519", ["Ed448"]="Ed448",
},
typ = nil,
issuer = nil,
audiences = nil,
subject = nil,
jwtid = nil,
ignore_not_before = false,
ignore_expiration = false,
current_unix_timestamp = nil,
timestamp_skew_seconds = 1,
}
使用对称密钥的最小示例:
local jwt = require("resty.jwt-verification")
local token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE3MTY2NTUwMTV9.NuEhIzUuufJgPZ8CmCPnD4Vrw7EnTyWD8bGtYCwuDZ0"
local decoded_token, err = jwt.verify(token, "superSecretKey")
if not decoded_token then
return nil, "invalid jwt: " .. err
end
print(decoded_token.header.alg) -- HS256
print(decoded_token.payload.foo) -- bar
使用非对称密钥的最小示例:
local jwt = require("resty.jwt-verification")
local token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE3MTY2Njg2Mzd9.H6PE-zLizMMqefx8DG4X5glVjyxR9UNT225Tq2yufHhu4k9K0IGttpykjMCG8Ck_4Qt2ezEWIgoiWhSn1rv_zwxe7Pv-B09fDs7h1hbASi5MZ0YVAmK9ID1RCKM_NTBEnPLot_iopKZRj2_J5F7lvXwJDZSzEAFJZdrgjKeBS4saDZAv7SIL9Nk75rdhgY-RgRwsjmTYSksj7eioRJJLHifrMnlQDbdrBD5_Qk5tD6VPcssO-vIVBUAYrYYTa7M7A_v47UH84zDtzNYBbk9NrDbyq5-tYs0lZwNhIX8t-0VAxjuCyrrGZvv8_O01pdi90kQmntFIbaiDiD-1WlGcGA"
local decoded_token, err = jwt.verify(token, "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvXFhNyhFWuWtFSJqfOAw\np42lLIn9kB9oaciiKgNAYZ8SYw5t9Fo+Zh7IciVijn+cVS2/aoBNg2HhfdYgfpQ/\nsb6jwbRqFMln2GmG+X2aJ2wXMJ/QfxrPFdO9L36bAEwkubUTYXwgMSm1KqWRN8xX\n+oBu+dbyzw7iUbrmw0ybzXKZLJvetCvmt0reU5TvdwoczOWFBSKeYnzBrC6hISD8\n8TYDJ4tiw1EWVOupQGqgel0KjC7iwdIYi7PROn6/1MMnF48zlBbT/7/zORj84Z/y\nDnmxZu1MQ07kHqXDRYumSfCerg5Xw5vde7Tz8O0TWtaYV3HJXNa0VpN5OI3L4y7P\nhwIDAQAB\n-----END PUBLIC KEY-----")
if not decoded_token then
return nil, "invalid jwt: " .. err
end
print(decoded_token.header.alg) -- RS256
print(decoded_token.payload.foo) -- bar
使用自定义 options 的示例:
local jwt = require("resty.jwt-verification")
local token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE3MTY2NTUwMTV9.NuEhIzUuufJgPZ8CmCPnD4Vrw7EnTyWD8bGtYCwuDZ0"
local decoded_token, err = jwt.verify(token, "superSecretKey", {
valid_signing_algorithms = {["HS256"]="HS256", ["HS384"]="HS384", ["HS512"]="HS512"}, -- 仅允许 HS 家族的 alg
audiences = {"user", "admin"}, -- `aud` 必须是以下之一
ignore_not_before = true -- 忽略 `nbf` 声明(不推荐)
})
if not decoded_token then
return nil, "invalid jwt: " .. err
end
print(decoded_token.header.alg) -- HS256
print(decoded_token.payload.foo) -- bar
jwt.decrypt
语法: decoded_token, err = jwt.decrypt(token, secret, options?)
解密并验证 JWE 令牌,并将其转换为 lua 表。
可选参数 options 可以传递以配置令牌验证器。有效字段包括:
- valid_encryption_alg_algorithms (dictalg 声明的字典。
- valid_encryption_enc_algorithms (dictenc 声明的字典。
- typ (string | nil): 如果不为 null,确保 JWT 声明 typ 匹配传递的值。
- issuer (string | nil): 如果不为 null,确保 JWT 声明 iss 匹配传递的值。
- audiences (string | table | nil): 如果不为 null,确保 JWT 声明 aud 匹配提供的值之一。
- subject (string | nil): 如果不为 null,确保 JWT 声明 sub 匹配传递的值。
- jwtid (string | nil): 如果不为 null,确保 JWT 声明 jti 匹配传递的值。
- ignore_not_before (bool): 如果为 true,则将忽略 JWT 声明 nbf。
- ignore_expiration (bool): 如果为 true,则将忽略 JWT 声明 exp。
- current_unix_timestamp (datetime | nil): JWT nbf 和 exp 声明将根据此时间戳进行验证。如果为 null,
将使用 ngx.time() 提供的当前日期时间。
- timestamp_skew_seconds (int): 库可以使用多少秒的宽限时间来检查令牌过期与当前时间的关系。当时钟并不总是完全同步时很有用。将此值设置得过高可能会带来安全问题。
- allow_nested_jwt (bool): 允许验证包含另一个 jwt 的 jwts(即嵌套 jwts 或 jwt-in-jwt)。这是可选的,因为要验证的声明始终在最内层的 jwt 中,并且不会自动验证。您需要递归验证此库返回的 payload 字段中的内层 jwts。嵌套的 jwt 必须包含将 cty 头键设置为 JWT 以被识别为此类。
options 字段的默认值:
local decrypt_default_options = {
valid_encryption_alg_algorithms = {
["RSA-OAEP"]="RSA-OAEP",
["RSA-OAEP-256"]="RSA-OAEP-256", ["RSA-OAEP-384"]="RSA-OAEP-384", ["RSA-OAEP-512"]="RSA-OAEP-512",
["A128KW"]="A128KW", ["A192KW"]="A192KW", ["A256KW"]="A256KW",
["dir"]="dir",
["ECDH-ES"]="ECDH-ES",
["ECDH-ES+A128KW"]="ECDH-ES+A128KW",
["ECDH-ES+A192KW"]="ECDH-ES+A192KW",
["ECDH-ES+A256KW"]="ECDH-ES+A256KW",
},
valid_encryption_enc_algorithms = {
["A128CBC-HS256"]="A128CBC-HS256",
["A192CBC-HS384"]="A192CBC-HS384",
["A256CBC-HS512"]="A256CBC-HS512",
["A128GCM"]="A128GCM",
["A192GCM"]="A192GCM",
["A256GCM"]="A256GCM",
},
typ = nil,
issuer = nil,
audiences = nil,
subject = nil,
jwtid = nil,
ignore_not_before = false,
ignore_expiration = false,
current_unix_timestamp = nil,
timestamp_skew_seconds = 1,
allow_nested_jwt = false,
}
使用对称密钥的最小示例:
local jwt = require("resty.jwt-verification")
local token = "eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.zAIq7qVAEO-eCG6gOdd3ld8_IHzeq3UlaWLHF2IDn6nNUuHh5n_i4w.5CM864cgiBgFPwluW4ViRg.mUeX7zHDVNsXhys0XO5S4w.t3yAR_HU0GDTEyCbpRa6BQ"
local decoded_token, err = jwt.decrypt(token, "superSecretKey12")
if not decoded_token then
return nil, "invalid jwt: " .. err
end
print(decoded_token.header.alg) -- A128KW
print(decoded_token.header.enc) -- A128CBC-HS256
print(decoded_token.payload.foo) -- bar
使用 PEM 格式的非对称密钥的最小示例:
local jwt = require("resty.jwt-verification")
local token = "eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImVuYyI6IkEyNTZHQ00iLCJlcGsiOnsieCI6IkFJdkVhSzVKZGl6d1I5ZFMzRUN2Y0dKMGNHWXNFejdpYWJwRUp1bE0tWDAiLCJjcnYiOiJYMjU1MTkiLCJrdHkiOiJPS1AifX0.QFfmPVYjk1PoyhE7elaDgUdUGGeAECLo7jB4ghq_8MIRXV3VKO1yAA.XITF2apHB5roeUsx.08T0gALwkb6Wibr2Og.IJoh3U_tspnMx_mWelRT5g"
local decoded_token, err = jwt.decrypt(token, "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VuBCIEIMCxXl/FEuh3pGo1Z++QRs2vudqkGd63mK0Js0f6y+55\n-----END PRIVATE KEY-----", nil)
if not decoded_token then
return nil, "invalid jwt: " .. err
end
print(decoded_token.header.alg) -- ECDH-ES+A128KW
print(decoded_token.header.enc) -- A256GCM
print(decoded_token.payload.foo) -- bar
使用自定义 options 的示例:
local jwt = require("resty.jwt-verification")
local token = "eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.zAIq7qVAEO-eCG6gOdd3ld8_IHzeq3UlaWLHF2IDn6nNUuHh5n_i4w.5CM864cgiBgFPwluW4ViRg.mUeX7zHDVNsXhys0XO5S4w.t3yAR_HU0GDTEyCbpRa6BQ"
local decoded_token, err = jwt.decrypt(token, "superSecretKey12", {
valid_encryption_alg_algorithms = {["A128KW"]="A128KW"}, -- 仅允许 A128KW 家族的 alg(需要 OpenSSL 3.0+)
valid_encryption_enc_algorithms = {["A128CBC-HS256"]="A128CBC-HS256"}, -- 仅允许 A128CBC 家族的 enc
audiences = {"user", "admin"}, -- `aud` 必须是以下之一
ignore_not_before = true -- 忽略 `nbf` 声明(不推荐)
})
if not decoded_token then
return nil, "invalid jwt: " .. err
end
print(decoded_token.header.alg) -- A128KW
print(decoded_token.header.enc) -- A128CBC-HS256
print(decoded_token.payload.foo) -- bar
JWKS 验证用法
resty.jwt-verification-jwks 模块实现了从 HTTP 端点自动检索 JWKS 并使用获取的密钥进行后续 JWT 验证。
resty.jwt-verification-jwks-cache-* 模块实现了可选的 JWKS 缓存策略。一次只能启用一个缓存策略;如果没有启用任何策略,则每个要验证的 JWT 都将调用 JWKS 端点一次。
jwks.init
语法: ok, err = jwks.init(cache_strategy?)
初始化 jwks 模块,并可选地指定缓存策略。
此函数只能调用一次,最好在 init_by_lua_file 部分中调用。
local jwks = require("resty.jwt-verification-jwks")
-- 无缓存初始化
local ok, err = jwks.init(nil)
if not ok then
ngx.say("初始化 jwks 模块时出错: ", err)
end
-- 或者...
-- 使用基于 openresty 共享内存字典的本地缓存进行初始化。
-- 在您的 nginx 配置的 `http` 部分添加:`lua_shared_dict resty_jwt_verification_cache_jwks 10m;`
-- 参见 https://openresty-reference.readthedocs.io/en/latest/Lua_Nginx_API/#ngxshareddict
local jwks_cache_local = require("resty.jwt-verification-jwks-cache-local")
local ok, err = jwks.init(jwks_cache_local)
if not ok then
ngx.say("初始化 jwks 模块时出错: ", err)
end
您可以实现自己的缓存并在初始化方法中传递。以下是示例:
local my_cache = {}
--- 获取键的缓存条目字符串。
---@param key string 缓存键。
---@return string|nil value 如果存在,则返回缓存结果作为字符串,否则返回 nil。
function my_cache.get(key)
-- TODO
end
--- 在键下缓存数据,直到过期。
---@param key string 缓存键。
---@param value string 缓存值。
---@param expiry integer 缓存条目过期时间(秒)。
---@return boolean|nil ok 成功时为 true
---@return string|nil err 成功时为 nil,否则为错误信息。
function my_cache.setex(key, value, expiry)
-- TODO
end
local ok, err = jwks.init(my_cache)
if not ok then
ngx.say("初始化 jwks 模块时出错: ", err)
end
jwks.set_http_timeouts_ms
语法: jwks.set_http_timeouts_ms(connect, send, read)
设置用于获取 JWKS 的 HTTP 客户端超时(以毫秒为单位)。
local jwks = require("resty.jwt-verification-jwks")
jwks.set_http_timeouts_ms(5000, 5000, 5000)
jwks.set_http_ssl_verify
语法: jwks.set_http_ssl_verify(enabled)
启用/禁用用于获取 JWKS 的 HTTP 客户端的 TLS 验证。
默认情况下,所有 TLS 证书都经过验证。如果 JWKS 端点使用自签名证书,请将相应的根 CA 添加到操作系统证书存储中,或者使用此端点禁用证书验证(这不安全)。
local jwks = require("resty.jwt-verification-jwks")
jwks.set_http_ssl_verify(false)
jwks.set_cache_ttl
语法: jwks.set_http_ssl_verify(enabled)
更改默认缓存 TTL。默认值为 12 小时。
注意: 只有在 jwks 模块已使用缓存初始化时,才能使用缓存 ttl。 参见 如何启用缓存。
local jwks = require("resty.jwt-verification-jwks")
jwks.set_cache_ttl(2 * 3600) -- 2小时
jwks.fetch_jwks
语法: payload, err = jwks.fetch_jwks(endpoint)
手动从 HTTP 端点获取 JWKS;成功时返回的有效负载是 HTTP 响应体的字符串: 不对有效负载是否包含 JWKS 或其他内容进行任何检查。
如果启用了缓存策略,端点将首先尝试从缓存中获取。如果缓存未命中并通过 HTTP 成功检索 JWKS,则缓存将使用结果进行更新。
local jwks = require("resty.jwt-verification-jwks")
payload, err = jwks.fetch_jwks("https://www.googleapis.com/oauth2/v3/certs")
if payload == nil then
print("获取 JWKS 失败: ", err)
return
end
print(payload) -- '{"keys":[{"alg":"RS256","e":"AQAB","kid":"882503a5fd56e9f734dfba5c50d7bf48db284ae9","kty":"RSA","n":"woRUr445_ODXrFeynz5L208aJkABOKQHEzbfGM_V1ijkYZWZKY0PXKPP_wRKcE4C6OyjDNd5gHh3dF5QsVhVDZCfR9QjTf94o4asngrHzdOcfQ0pZIvzu_vzaVG82VGLM-2rKQp8uz06A6TbUzbIv9wQ8wQpYDIdujNkLqL22Mkb2drPxm9Y9I05PmVdkkvAbu4Q_KRJWxykOigHp-hVBmpYS2P3xuX56gM7ZRcXXJKKUfrGel4nDhSIAAD1wBNcVVgKbb0TYfZmVpRSCji_b6JHjqYhYjUasdotYJzWl7quAFsN_X_4j-cHZ30OS81j--OiIxWpL11y1kcbE0u-Dw","use":"sig"},{"n":"m7GlcF1ExRB4braT7sDnZvlY3wpqX9krkVRqcVA-m43FWFYBtuSpd-lc0EV8R8TO180y0tSgJc7hviI1IBJQlNa7XkjVGhY0ZFUp5rTpC45QbA9Smo4CLa5HQIf-69rkkovjFNMuDQvNiYCgRPLyRjmQbN2uHl4fU3hhf5qFqKTKo7eLCZiEMjrOkTXziA7xJJigUGe-ab8U-AXNH1fnCbejzHEIxL0eUG_4r4xddImOxETDO5T65AQCeqs7vtYos2xq5SLFuaUsithRQ-IMm3OlcVhMjBYt6uvGS6IdMjKon4wThCxEqAEXg0nahiGjnQCW176SNF152__TOjQVwQ","alg":"RS256","kty":"RSA","use":"sig","kid":"8e8fc8e556f7a76d08d35829d6f90ae2e12cfd0d","e":"AQAB"}]}'
jwks.verify_jwt_with_jwks
语法: jwt, err = jwks.verify_jwt_with_jwks(jwt_token, jwks_endpoint, jws_options?)
给定一个作为字符串的签名 jwt_token,使用在 jwks_endpoint 找到的 HTTP 服务提供的 JWKS 验证其签名。
成功时,验证后的 JWT 作为 lua 表返回,否则返回 nil 和错误。
可选参数 jws_options 可以传递以配置令牌验证器,在成功获取 JWKS 后调用 jwt.verify。有关可以传递哪些选项的更多信息,请参见 jwt.verify 的相关文档。
local jwks = require("resty.jwt-verification-jwks")
jwt, err = jwks.verify_jwt_with_jwks("<MY_JWT>", "http://myservice:8888/.well-known/jwks.json", nil)
if jwt == nil then
print("验证 jwt 失败: ", err)
return
end
print(jwt.header.alg)
print(tostring(jwt.payload))
jwks.decrypt_jwt_with_jwks
语法: jwt, err = jwks.decrypt_jwt_with_jwks(jwt_token, jwks_endpoint, jwe_options?)
给定一个作为字符串的加密 jwt_token,使用在 jwks_endpoint 找到的 HTTP 服务提供的 JWKS 解密它。
成功时,解密后的 JWT 作为 lua 表返回,否则返回 nil 和错误。
可选参数 jwe_options 可以传递以配置令牌验证器,在成功获取 JWKS 后调用 jwt.decrypt。有关可以传递哪些选项的更多信息,请参见 jwt.decrypt 的相关文档。
local jwks = require("resty.jwt-verification-jwks")
jwt, err = jwks.decrypt_jwt_with_jwks("<MY_JWT>", "http://myservice:8888/.well-known/jwks.json", nil)
if jwt == nil then
print("解密 jwt 失败: ", err)
return
end
print(jwt.header.alg)
print(jwt.header.enc)
print(tostring(jwt.payload))
参考的 RFC
- RFC 7515 JSON Web Signature (JWS)
- RFC 7516 JSON Web Encryption (JWE)
- RFC 7517 JSON Web Key (JWK)
- RFC 7518 JSON Web Algorithms (JWA)
- RFC 7519 JSON Web Token (JWT)
- RFC 7520 使用 JSON 对象签名和加密保护内容的示例 (JOSE)
- IANA JOSE 分配
运行测试
设置
安装测试套件:
sudo cpan Test::Nginx
安装 openresty:请参见 https://openresty.org/en/linux-packages.html
运行
export PATH=/usr/local/openresty/nginx/sbin:$PATH
prove -r t
运行基准测试
测试套件 Test::Nginx 内置了与 weighttp 的基准测试集成。
增加 sysctl 限制
如果您计划对库进行压力测试,您可能需要增加系统限制。
cat > /etc/sysctl.d/openresty-benchmarks.conf << EOF
net.ipv4.ip_local_port_range=2048 65535
net.ipv4.tcp_tw_reuse=1
net.core.netdev_max_backlog=2000
net.ipv4.tcp_max_syn_backlog=2048
EOF
## 应用更改
sudo sysctl -p /etc/sysctl.d/openresty-benchmarks.conf
启动测试
我在 benchmarks 文件夹中创建了一些伪真实世界场景。
## 有关语法的更多信息: https://openresty.gitbooks.io/programming-openresty/content/testing/test-modes.html
export TEST_NGINX_BENCHMARK='50000 10'
prove -r ./benchmarks
默认情况下,将使用 1 个 nginx 工作进程和 1 个 CPU 核心来执行基准测试。要增加工作进程限制,请更改 .t 文件中的 workers(1); 指令并重新运行基准测试。
GitHub
您可以在 nginx-module-jwt-verification 的 GitHub 仓库中找到此模块的其他配置提示和文档。