Перейти к содержанию

jwt-verification: Библиотека проверки JWT для nginx-module-lua с интеграцией 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

Чтобы использовать эту библиотеку Lua с NGINX, убедитесь, что nginx-module-lua установлен.

Этот документ описывает lua-resty-jwt-verification v0.7.0, выпущенную 30 октября 2025 года.


Библиотека проверки JWT для OpenResty.

OPM LuaRocks Tests

Описание

Библиотека проверки JWT для OpenResty.

Цель проекта — быть современным и более легким заменителем lua-resty-jwt с встроенной поддержкой JWKS.

Этот проект не предоставляет функции манипуляции или создания JWT: вы можете только проверять/расшифровывать токены.

Непредназначенные цели библиотеки

  • Создание/изменение JWT
  • Полная функциональность ради полноты RFC.
  • Бессмысленные и небезопасные функции RFC (например, alg none) не будут реализованы.

Отличия от lua-resty-jwt

Основные отличия: - Нет манипуляций с JWT (вы можете только расшифровывать/проверять их) - Более простая внутренняя структура, основанная на более современных версиях lua-resty-openssl и OpenSSL. - Поддерживает различные алгоритмы JWE (см. таблицы выше). - Автоматическая проверка JWT с учетом HTTP-эндпоинта JWKS.

Если какой-либо из вышеуказанных пунктов является проблемой или вам нужна совместимость со старыми версиями OpenResty, я рекомендую остаться с lua-resty-jwt.

Типы

Типы и проверки на null предоставляются с обширным использованием EmmyLua annotations.

Интеграции плагинов IDE для EmmyLua: - 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 ❌ Необязательно

*Первый официальный релиз OpenResty, включающий OpenSSL 3.0+, это 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, "некорректный 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 (dict | nil): словарь, содержащий допустимые утверждения alg, используемые для проверки JWT. - 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, "некорректный 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, "некорректный 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
    audiences = {"user", "admin"}, -- `aud` должен быть одним из следующих
    ignore_not_before = true -- игнорировать утверждение `nbf` (не рекомендуется)
})
if not decoded_token then
    return nil, "некорректный 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 (dict | nil): словарь, содержащий допустимые утверждения alg, используемые для расшифровки JWT. - valid_encryption_enc_algorithms (dict | nil): словарь, содержащий допустимые утверждения enc, используемые для расшифровки JWT. - 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, содержащих другой jwt (также известные как вложенные jwt или jwt-в-jwt). Это опционально, поскольку утверждения для проверки всегда находятся внутри самого внутреннего jwt и НЕ БУДУТ автоматически проверены. Вам нужно рекурсивно проверять внутренние jwt, возвращаемые в виде строки в поле payload этой библиотеки. Вложенный 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, "некорректный 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, "некорректный 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 (требует OpenSSL 3.0+)
    valid_encryption_enc_algorithms = {["A128CBC-HS256"]="A128CBC-HS256"}, -- разрешить только шифры семейства A128CBC
    audiences = {"user", "admin"}, -- `aud` должен быть одним из следующих
    ignore_not_before = true -- игнорировать утверждение `nbf` (не рекомендуется)
})
if not decoded_token then
    return nil, "некорректный 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 реализует автоматическую выборку JWKS с HTTP-эндпоинта и последующую проверку JWT с использованием полученных ключей.

Модули resty.jwt-verification-jwks-cache-* реализуют необязательные стратегии кэширования JWKS. Включить можно только одну стратегию кэширования за раз; если ни одна не включена, эндпоинт JWKS будет вызываться один раз для каждого JWT для проверки.

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.
-- добавьте это в раздел `http` вашего конфигурационного файла nginx: `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)

Установить таймауты HTTP-клиента в миллисекундах, используемые для получения JWKS.

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)

Включить/выключить проверку TLS, используемую HTTP-клиентом для получения JWKS.

По умолчанию все сертификаты 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 часов.

Примечание: Время жизни кэша (ttl) может использоваться только тогда, когда модуль jwks был инициализирован с кэшем. См. как включить кэширование.

local jwks = require("resty.jwt-verification-jwks")

jwks.set_cache_ttl(2 * 3600) -- 2ч

jwks.fetch_jwks

синтаксис: payload, err = jwks.fetch_jwks(endpoint)

Вручную получить JWKS с HTTP-эндпоинта; возвращаемый payload, в случае успеха, является телом HTTP-ответа в виде строки: Никакая проверка не выполняется, чтобы определить, содержит ли payload JWKS или что-то другое.

Если стратегия кэширования была включена, эндпоинт сначала попытается получить его из кэша. После промаха кэша и успешной выборки JWKS через HTTP кэш будет обновлен результатом.

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, предоставленных HTTP-сервисом, найденным по адресу jwks_endpoint.

В случае успеха проверенный JWT возвращается в виде таблицы lua, в противном случае возвращается nil и ошибка.

Необязательный параметр jws_options может быть передан для настройки валидатора токена при вызове jwt.verify после успешной выборки JWKS. См. соответствующую документацию 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, предоставленных HTTP-сервисом, найденным по адресу jwks_endpoint.

В случае успеха расшифрованный JWT возвращается в виде таблицы lua, в противном случае возвращается nil и ошибка.

Необязательный параметр jwe_options может быть передан для настройки валидатора токена при вызове jwt.decrypt после успешной выборки JWKS. См. соответствующую документацию 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 assignments

Запуск тестов

Настройка

Установите тестовый пакет:

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 для выполнения бенчмарков. Чтобы увеличить лимиты рабочих процессов, измените директиву workers(1); внутри файлов .t и повторно запустите бенчмарк.

GitHub

Вы можете найти дополнительные советы по конфигурации и документацию для этого модуля в репозитории GitHub для nginx-module-jwt-verification.