session: Библиотека сессий для nginx-module-lua – гибкая и безопасная
Установка
Если вы еще не настроили подписку на 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-session
CentOS/RHEL 8+, Fedora Linux, Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install lua5.1-resty-session
Чтобы использовать эту библиотеку Lua с NGINX, убедитесь, что nginx-module-lua установлен.
Этот документ описывает lua-resty-session v4.1.5, выпущенную 24 ноября 2025 года.
lua-resty-session — это безопасная и гибкая библиотека сессий для OpenResty.
TL;DR;
- Сессии неизменяемы (каждое сохранение генерирует новую сессию) и без блокировок.
- Данные сессии шифруются с помощью AES-256-GCM с ключом, полученным с использованием HKDF-SHA256 (в режиме FIPS используется PBKDF2 с SHA-256).
- Сессия имеет фиксированный заголовок, который защищен HMAC-SHA256 MAC с ключом, полученным с использованием HKDF-SHA256 (в режиме FIPS используется PBKDF2 с SHA-256).
- Данные сессии могут храниться в безсостоянии cookie или в различных бэкенд-хранилищах.
- Один cookie сессии может поддерживать несколько сессий для разных аудиторий.
Примечание: Версия 4.0.0 была переписана с учетом множества уроков, усвоенных за годы. Если вы все еще используете более старую версию, пожалуйста, обратитесь к старой документации.
Синопсис
worker_processes 1;
events {
worker_connections 1024;
}
http {
init_by_lua_block {
require "resty.session".init({
remember = true,
audience = "demo",
secret = "RaJKp8UQW1",
storage = "cookie",
})
}
server {
listen 8080;
server_name localhost;
default_type text/html;
location / {
content_by_lua_block {
ngx.say([[
<html>
<body>
<a href=/start>Начать тест</a>
</body>
</html>
]])
}
}
location /start {
content_by_lua_block {
local session = require "resty.session".new()
session:set_subject("Фанат OpenResty")
session:set("quote", "Быстрая коричневая лиса перепрыгивает через ленивую собаку")
local ok, err = session:save()
ngx.say(string.format([[
<html>
<body>
<p>Сессия начата (%s)</p>
<p><a href=/started>Проверить, действительно ли это было</a></p>
</body>
</html>
]], err or "нет ошибок"))
}
}
location /started {
content_by_lua_block {
local session, err = require "resty.session".start()
ngx.say(string.format([[
<html>
<body>
<p>Сессия была начата %s (%s)</p>
<p><blockquote>%s</blockquote></p>
<p><a href=/modify>Изменить сессию</a></p>
</body>
</html>
]],
session:get_subject() or "Аноним",
err or "нет ошибок",
session:get("quote") or "нет цитаты"
))
}
}
location /modify {
content_by_lua_block {
local session, err = require "resty.session".start()
session:set_subject("Фанат Lua")
session:set("quote", "Lorem ipsum dolor sit amet")
local _, err_save = session:save()
ngx.say(string.format([[
<html>
<body>
<p>Сессия была изменена (%s)</p>
<p><a href=/modified>Проверить, изменена ли она</a></p>
</body>
</html>
]], err or err_save or "нет ошибок"))
}
}
location /modified {
content_by_lua_block {
local session, err = require "resty.session".start()
ngx.say(string.format([[
<html>
<body>
<p>Сессия была начата %s (%s)</p>
<p><blockquote>%s</blockquote></p>
<p><a href=/destroy>Уничтожить сессию</a></p>
</body>
</html>
]],
session:get_subject() or "Аноним",
err or "нет ошибок",
session:get("quote") or "нет цитаты"
))
}
}
location /destroy {
content_by_lua_block {
local ok, err = require "resty.session".destroy()
ngx.say(string.format([[
<html>
<body>
<p>Сессия была уничтожена (%s)</p>
<p><a href=/destroyed>Проверить, действительно ли это было?</a></p>
</body>
</html>
]], err or "нет ошибок"))
}
}
location /destroyed {
content_by_lua_block {
local session, err = require "resty.session".open()
ngx.say(string.format([[
<html>
<body>
<p>Сессия действительно была уничтожена, вы известны как %s (%s)</p>
<p><a href=/>Начать снова</a></p>
</body>
</html>
]],
session:get_subject() or "Аноним",
err or "нет ошибок"
))
}
}
}
}
Конфигурация
Конфигурацию можно разделить на общую конфигурацию сессий и конфигурацию хранилища на стороне сервера.
Вот пример:
init_by_lua_block {
require "resty.session".init({
remember = true,
store_metadata = true,
secret = "RaJKp8UQW1",
secret_fallbacks = {
"X88FuG1AkY",
"fxWNymIpbb",
},
storage = "postgres",
postgres = {
username = "my-service",
password = "kVgIXCE5Hg",
database = "sessions",
},
})
}
Конфигурация сессии
Конфигурация сессии может быть передана в функции инициализации, конструктора и помощника.
Вот возможные параметры конфигурации сессии:
| Параметр | По умолчанию | Описание |
|---|---|---|
secret |
nil |
Секрет, используемый для получения ключа. Секрет хешируется с помощью SHA-256 перед использованием. Например, "RaJKp8UQW1". |
secret_fallbacks |
nil |
Массив секретов, которые могут использоваться в качестве альтернативных секретов (при ротации ключей), например, { "6RfrAYYzYq", "MkbTkkyF9C" }. |
ikm |
(случайный) | Начальный ключевой материал (или ikm) может быть указан напрямую (без использования секрета) с точно 32 байтами данных. Например, "5ixIW4QVMk0dPtoIhn41Eh1I9enP2060" |
ikm_fallbacks |
nil |
Массив начальных ключевых материалов, которые могут использоваться в качестве альтернативных ключей (при ротации ключей), например, { "QvPtlPKxOKdP5MCu1oI3lOEXIVuDckp7" }. |
cookie_prefix |
nil |
Префикс cookie, используйте nil, "__Host-" или "__Secure-". |
cookie_name |
"session" |
Имя cookie сессии, например, "session". |
cookie_path |
"/" |
Путь cookie, например, "/". |
cookie_domain |
nil |
Домен cookie, например, "example.com" |
cookie_http_only |
true |
Установить cookie только для HTTP, используйте true или false. |
cookie_secure |
nil |
Установить cookie как безопасное, используйте nil, true или false. |
cookie_priority |
nil |
Приоритет cookie, используйте nil, "Low", "Medium" или "High". |
cookie_same_site |
"Lax" |
Политика same-site для cookie, используйте nil, "Lax", "Strict", "None" или "Default" |
cookie_same_party |
nil |
Установить флаг cookie с тем же участником, используйте nil, true или false. |
cookie_partitioned |
nil |
Установить флаг cookie с разделением, используйте nil, true или false. |
remember |
false |
Включить или отключить постоянные сессии, используйте nil, true или false. |
remember_safety |
"Medium" |
Сложность получения ключа cookie для запоминания, используйте nil, "None" (быстро), "Low", "Medium", "High" или "Very High" (медленно). |
remember_cookie_name |
"remember" |
Имя cookie для постоянной сессии, например, "remember". |
audience |
"default" |
Аудитория сессии, например, "my-application". |
subject |
nil |
Тема сессии, например, "[email protected]". |
enforce_same_subject |
false |
Если установлено в true, аудитории должны делить одну и ту же тему. Библиотека удаляет данные аудитории, которые не соответствуют теме, при сохранении. |
stale_ttl |
10 |
Когда сессия сохраняется, создается новая сессия, stale ttl указывает, как долго старая сессия может быть использована, например, 10 (в секундах). |
idling_timeout |
900 |
Таймаут бездействия указывает, как долго сессия может быть неактивной, прежде чем она будет считаться недействительной, например, 900 (15 минут) (в секундах), 0 отключает проверки и обновления. |
rolling_timeout |
3600 |
Таймаут обновления указывает, как долго сессия может использоваться, прежде чем ее необходимо обновить, например, 3600 (час) (в секундах), 0 отключает проверки и обновления. |
absolute_timeout |
86400 |
Абсолютный таймаут ограничивает, как долго сессия может быть обновлена, прежде чем потребуется повторная аутентификация, например, 86400 (сутки) (в секундах), 0 отключает проверки. |
remember_rolling_timeout |
604800 |
Таймаут запоминания указывает, как долго постоянная сессия считается действительной, например, 604800 (неделя) (в секундах), 0 отключает проверки и обновления. |
remember_absolute_timeout |
2592000 |
Абсолютный таймаут запоминания ограничивает, как долго постоянная сессия может быть обновлена, прежде чем потребуется повторная аутентификация, например, 2592000 (30 дней) (в секундах), 0 отключает проверки. |
hash_storage_key |
false |
Указывать, хешировать ли ключ хранилища. С хешированным ключом хранилища невозможно расшифровать данные на стороне сервера без наличия cookie, используйте nil, true или false. |
hash_subject |
false |
Указывать, хешировать ли тему, когда store_metadata включен, например, по причинам PII. |
store_metadata |
false |
Указывать, нужно ли также хранить метаданные сессий, такие как сбор данных сессий для конкретной аудитории, принадлежащей конкретной теме. |
touch_threshold |
60 |
Порог обновления контролирует, как часто или редко session:refresh обновляет cookie, например, 60 (одна минута) (в секундах). |
compression_threshold |
1024 |
Порог сжатия контролирует, когда данные сжимаются, например, 1024 (килобайт) (в байтах), 0 отключает сжатие. |
bind |
nil |
Привязать сессию к данным, полученным из HTTP-запроса или соединения, используйте ip, scheme, user-agent. Например, { "scheme", "user-agent" } будет рассчитывать MAC, используя также HTTP-запрос Scheme и заголовок User-Agent. |
request_headers |
nil |
Набор заголовков для отправки на upstream, используйте id, audience, subject, timeout, idling-timeout, rolling-timeout, absolute-timeout. Например, { "id", "timeout" } установит заголовки запроса Session-Id и Session-Timeout, когда вызывается set_headers. |
response_headers |
nil |
Набор заголовков для отправки downstream, используйте id, audience, subject, timeout, idling-timeout, rolling-timeout, absolute-timeout. Например, { "id", "timeout" } установит заголовки ответа Session-Id и Session-Timeout, когда вызывается set_headers. |
storage |
nil |
Хранилище отвечает за хранение данных сессии, используйте nil или "cookie" (данные хранятся в cookie), "dshm", "file", "memcached", "mysql", "postgres", "redis" или "shm", или укажите имя пользовательского модуля ("custom-storage"), или таблицу, реализующую интерфейс хранения сессий. |
dshm |
nil |
Конфигурация для хранилища dshm, например, { prefix = "sessions" } (см. ниже). |
file |
nil |
Конфигурация для файлового хранилища, например, { path = "/tmp", suffix = "session" } (см. ниже). |
memcached |
nil |
Конфигурация для хранилища memcached, например, { prefix = "sessions" } (см. ниже). |
mysql |
nil |
Конфигурация для хранилища MySQL / MariaDB, например, { database = "sessions" } (см. ниже). |
postgres |
nil |
Конфигурация для хранилища Postgres, например, { database = "sessions" } (см. ниже). |
redis |
nil |
Конфигурация для хранилищ Redis / Redis Sentinel / Redis Cluster, например, { prefix = "sessions" } (см. ниже). |
shm |
nil |
Конфигурация для хранилища общей памяти, например, { zone = "sessions" } |
["custom-storage"] |
nil |
Конфигурация пользовательского хранилища (загружается с помощью require "custom-storage"). |
Конфигурация хранилища cookie
При хранении данных в cookie дополнительная конфигурация не требуется, просто установите storage в nil или "cookie".
Конфигурация хранилища DSHM
С хранилищем DHSM вы можете использовать следующие настройки (установите storage в "dshm"):
| Параметр | По умолчанию | Описание |
|---|---|---|
prefix |
nil |
Префикс для ключей, хранящихся в DSHM. |
suffix |
nil |
Суффикс для ключей, хранящихся в DSHM. |
host |
"127.0.0.1" |
Хост для подключения. |
port |
4321 |
Порт для подключения. |
connect_timeout |
nil |
Управляет значением таймаута по умолчанию, используемым в методе connect объекта TCP/unix-domain socket. |
send_timeout |
nil |
Управляет значением таймаута по умолчанию, используемым в методе send объекта TCP/unix-domain socket. |
read_timeout |
nil |
Управляет значением таймаута по умолчанию, используемым в методе receive объекта TCP/unix-domain socket. |
keepalive_timeout |
nil |
Управляет максимальным временем простоя соединений в пуле соединений. |
pool |
nil |
Пользовательское имя для используемого пула соединений. |
pool_size |
nil |
Размер пула соединений. |
backlog |
nil |
Размер очереди, используемой, когда пул соединений полон (настраивается с помощью pool_size). |
ssl |
nil |
Включить SSL. |
ssl_verify |
nil |
Проверить сертификат сервера. |
server_name |
nil |
Имя сервера для нового расширения TLS Server Name Indication (SNI). |
Пожалуйста, обратитесь к ngx-distributed-shm для установки необходимых зависимостей.
Конфигурация файлового хранилища
С файловым хранилищем вы можете использовать следующие настройки (установите storage в "file"):
| Параметр | По умолчанию | Описание |
|---|---|---|
prefix |
nil |
Префикс файла для файла сессии. |
suffix |
nil |
Суффикс файла (или расширение без .) для файла сессии. |
pool |
nil |
Имя пула потоков, под которым происходит запись в файл (доступно только на Linux). |
path |
(tmp directory) | Путь (или директория), в которой создаются файлы сессий. |
Реализация требует LuaFileSystem, который вы можете установить с помощью LuaRocks:
❯ luarocks install LuaFileSystem
Конфигурация хранилища Memcached
С файловым Memcached вы можете использовать следующие настройки (установите storage в "memcached"):
| Параметр | По умолчанию | Описание |
|---|---|---|
prefix |
nil |
Префикс для ключей, хранящихся в memcached. |
suffix |
nil |
Суффикс для ключей, хранящихся в memcached. |
host |
127.0.0.1 |
Хост для подключения. |
port |
11211 |
Порт для подключения. |
socket |
nil |
Файл сокета для подключения. |
connect_timeout |
nil |
Управляет значением таймаута по умолчанию, используемым в методе connect объекта TCP/unix-domain socket. |
send_timeout |
nil |
Управляет значением таймаута по умолчанию, используемым в методе send объекта TCP/unix-domain socket. |
read_timeout |
nil |
Управляет значением таймаута по умолчанию, используемым в методе receive объекта TCP/unix-domain socket. |
keepalive_timeout |
nil |
Управляет максимальным временем простоя соединений в пуле соединений. |
pool |
nil |
Пользовательское имя для используемого пула соединений. |
pool_size |
nil |
Размер пула соединений. |
backlog |
nil |
Размер очереди, используемой, когда пул соединений полон (настраивается с помощью pool_size). |
ssl |
false |
Включить SSL. |
ssl_verify |
nil |
Проверить сертификат сервера. |
server_name |
nil |
Имя сервера для нового расширения TLS Server Name Indication (SNI). |
Конфигурация MySQL / MariaDB
С файловым MySQL / MariaDB вы можете использовать следующие настройки (установите storage в "mysql"):
| Параметр | По умолчанию | Описание |
|---|---|---|
host |
"127.0.0.1" |
Хост для подключения. |
port |
3306 |
Порт для подключения. |
socket |
nil |
Файл сокета для подключения. |
username |
nil |
Имя пользователя базы данных для аутентификации. |
password |
nil |
Пароль для аутентификации, может потребоваться в зависимости от конфигурации сервера. |
charset |
"ascii" |
Набор символов, используемый при подключении к MySQL. |
database |
nil |
Имя базы данных для подключения. |
table_name |
"sessions" |
Имя таблицы базы данных, в которую будут храниться данные сессий. |
table_name_meta |
"sessions_meta" |
Имя таблицы метаданных базы данных, в которую будут храниться метаданные сессий. |
max_packet_size |
1048576 |
Верхний предел для ответных пакетов, отправляемых от сервера MySQL (в байтах). |
connect_timeout |
nil |
Управляет значением таймаута по умолчанию, используемым в методе connect объекта TCP/unix-domain socket. |
send_timeout |
nil |
Управляет значением таймаута по умолчанию, используемым в методе send объекта TCP/unix-domain socket. |
read_timeout |
nil |
Управляет значением таймаута по умолчанию, используемым в методе receive объекта TCP/unix-domain socket. |
keepalive_timeout |
nil |
Управляет максимальным временем простоя соединений в пуле соединений. |
pool |
nil |
Пользовательское имя для используемого пула соединений. |
pool_size |
nil |
Размер пула соединений. |
backlog |
nil |
Размер очереди, используемой, когда пул соединений полон (настраивается с помощью pool_size). |
ssl |
false |
Включить SSL. |
ssl_verify |
nil |
Проверить сертификат сервера. |
Вам также необходимо создать следующие таблицы в вашей базе данных:
--
-- Таблица базы данных, которая хранит данные сессий.
--
CREATE TABLE IF NOT EXISTS sessions (
sid CHAR(43) PRIMARY KEY,
name VARCHAR(255),
data MEDIUMTEXT,
exp DATETIME,
INDEX (exp)
) CHARACTER SET ascii;
--
-- Таблица метаданных сессий.
--
-- Это необходимо только в том случае, если вы хотите хранить метаданные сессий.
--
CREATE TABLE IF NOT EXISTS sessions_meta (
aud VARCHAR(255),
sub VARCHAR(255),
sid CHAR(43),
PRIMARY KEY (aud, sub, sid),
CONSTRAINT FOREIGN KEY (sid) REFERENCES sessions(sid) ON DELETE CASCADE ON UPDATE CASCADE
) CHARACTER SET ascii;
Конфигурация Postgres
С файловым Postgres вы можете использовать следующие настройки (установите storage в "postgres"):
| Параметр | По умолчанию | Описание |
|---|---|---|
host |
"127.0.0.1" |
Хост для подключения. |
port |
5432 |
Порт для подключения. |
application |
5432 |
Установите имя соединения, отображаемое в pg_stat_activity (по умолчанию "pgmoon"). |
username |
"postgres" |
Имя пользователя базы данных для аутентификации. |
password |
nil |
Пароль для аутентификации, может потребоваться в зависимости от конфигурации сервера. |
database |
nil |
Имя базы данных для подключения. |
table_name |
"sessions" |
Имя таблицы базы данных, в которую будут храниться данные сессий (может быть префиксированным schema). |
table_name_meta |
"sessions_meta" |
Имя таблицы метаданных базы данных, в которую будут храниться метаданные сессий (может быть префиксированным schema). |
connect_timeout |
nil |
Управляет значением таймаута по умолчанию, используемым в методе connect объекта TCP/unix-domain socket. |
send_timeout |
nil |
Управляет значением таймаута по умолчанию, используемым в методе send объекта TCP/unix-domain socket. |
read_timeout |
nil |
Управляет значением таймаута по умолчанию, используемым в методе receive объекта TCP/unix-domain socket. |
keepalive_timeout |
nil |
Управляет максимальным временем простоя соединений в пуле соединений. |
pool |
nil |
Пользовательское имя для используемого пула соединений. |
pool_size |
nil |
Размер пула соединений. |
backlog |
nil |
Размер очереди, используемой, когда пул соединений полон (настраивается с помощью pool_size). |
ssl |
false |
Включить SSL. |
ssl_verify |
nil |
Проверить сертификат сервера. |
ssl_required |
nil |
Прервать соединение, если сервер не поддерживает SSL-соединения. |
Вам также необходимо создать следующие таблицы в вашей базе данных:
--
-- Таблица базы данных, которая хранит данные сессий.
--
CREATE TABLE IF NOT EXISTS sessions (
sid TEXT PRIMARY KEY,
name TEXT,
data TEXT,
exp TIMESTAMP WITH TIME ZONE
);
CREATE INDEX ON sessions (exp);
--
-- Таблица метаданных сессий.
--
-- Это необходимо только в том случае, если вы хотите хранить метаданные сессий.
--
CREATE TABLE IF NOT EXISTS sessions_meta (
aud TEXT,
sub TEXT,
sid TEXT REFERENCES sessions (sid) ON DELETE CASCADE ON UPDATE CASCADE,
PRIMARY KEY (aud, sub, sid)
);
Реализация требует pgmoon, который вы можете установить с помощью LuaRocks:
❯ luarocks install pgmoon
Конфигурация Redis
Библиотека сессий поддерживает соединения с одиночным Redis, Redis Sentinel и Redis Cluster. Общие параметры конфигурации для всех:
| Параметр | По умолчанию | Описание |
|---|---|---|
prefix |
nil |
Префикс для ключей, хранящихся в Redis. |
suffix |
nil |
Суффикс для ключей, хранящихся в Redis. |
username |
nil |
Имя пользователя базы данных для аутентификации. |
password |
nil |
Пароль для аутентификации. |
connect_timeout |
nil |
Управляет значением таймаута по умолчанию, используемым в методе connect объекта TCP/unix-domain socket. |
send_timeout |
nil |
Управляет значением таймаута по умолчанию, используемым в методе send объекта TCP/unix-domain socket. |
read_timeout |
nil |
Управляет значением таймаута по умолчанию, используемым в методе receive объекта TCP/unix-domain socket. |
keepalive_timeout |
nil |
Управляет максимальным временем простоя соединений в пуле соединений. |
pool |
nil |
Пользовательское имя для используемого пула соединений. |
pool_size |
nil |
Размер пула соединений. |
backlog |
nil |
Размер очереди, используемой, когда пул соединений полон (настраивается с помощью pool_size). |
ssl |
false |
Включить SSL. |
ssl_verify |
nil |
Проверить сертификат сервера. |
server_name |
nil |
Имя сервера для нового расширения TLS Server Name Indication (SNI). |
Реализация single redis выбирается, когда вы не передаете ни sentinels, ни nodes, что приведет к выбору реализации sentinel или cluster.
Конфигурация одиночного Redis
Одиночный Redis имеет следующие дополнительные параметры конфигурации (установите storage в "redis"):
| Параметр | По умолчанию | Описание |
|---|---|---|
host |
"127.0.0.1" |
Хост для подключения. |
port |
6379 |
Порт для подключения. |
socket |
nil |
Файл сокета для подключения. |
Конфигурация Redis Sentinel
Redis Sentinel имеет следующие дополнительные параметры конфигурации (установите storage в "redis" и настройте sentinels):
| Параметр | По умолчанию | Описание |
|---|---|---|
master |
nil |
Имя мастера. |
role |
nil |
"master" или "slave". |
socket |
nil |
Файл сокета для подключения. |
sentinels |
nil |
Redis Sentinels. |
sentinel_username |
nil |
Необязательное имя пользователя sentinel. |
sentinel_password |
nil |
Необязательный пароль sentinel. |
database |
nil |
База данных для подключения. |
sentinels — это массив записей Sentinel:
| Параметр | По умолчанию | Описание |
|---|---|---|
host |
nil |
Хост для подключения. |
port |
nil |
Порт для подключения. |
Реализация sentinel выбирается, когда вы передаете sentinels как часть конфигурации redis (и не передаете nodes, что выберет реализацию cluster).
Реализация требует lua-resty-redis-connector, который вы можете установить с помощью LuaRocks:
❯ luarocks install lua-resty-redis-connector
Конфигурация Redis Cluster
Redis Cluster имеет следующие дополнительные параметры конфигурации (установите storage в "redis" и настройте nodes):
| Параметр | По умолчанию | Описание |
|---|---|---|
name |
nil |
Имя кластера Redis. |
nodes |
nil |
Узлы кластера Redis. |
lock_zone |
nil |
Имя общей памяти для блокировок. |
lock_prefix |
nil |
Префикс имени общей памяти для блокировок. |
max_redirections |
nil |
Максимальное количество попыток для перенаправления. |
max_connection_attempts |
nil |
Максимальное количество попыток для соединения. |
max_connection_timeout |
nil |
Максимальное время ожидания соединения в целом среди попыток. |
nodes — это массив записей узлов кластера:
| Параметр | По умолчанию | Описание |
|---|---|---|
ip |
"127.0.0.1" |
IP-адрес для подключения. |
port |
6379 |
Порт для подключения. |
Реализация cluster выбирается, когда вы передаете nodes как часть конфигурации redis.
Для правильной работы cluster вам необходимо настроить lock_zone, поэтому добавьте это также в вашу конфигурацию Nginx:
lua_shared_dict redis_cluster_locks 100k;
И установите lock_zone в "redis_cluster_locks".
Реализация требует resty-redis-cluster или kong-redis-cluster, которые вы можете установить с помощью LuaRocks:
❯ luarocks install resty-redis-cluster
## или
❯ luarocks install kong-redis-cluster
Конфигурация SHM
С хранилищем SHM вы можете использовать следующие настройки (установите storage в "shm"):
| Параметр | По умолчанию | Описание |
|---|---|---|
prefix |
nil |
Префикс для ключей, хранящихся в SHM. |
suffix |
nil |
Суффикс для ключей, хранящихся в SHM. |
zone |
"sessions" |
Имя зоны общей памяти. |
Вам также нужно создать общую словарную зону zone в Nginx:
lua_shared_dict sessions 10m;
Примечание: вам может потребоваться настроить размер зоны общей памяти в зависимости от ваших потребностей.
API
Сгенерированные LDoc API-документы также можно просмотреть по адресу bungle.github.io/lua-resty-session.
Инициализация
session.init
синтаксис: session.init(configuration)
Инициализирует библиотеку сессий.
Эта функция может быть вызвана на фазах init или init_worker в OpenResty, чтобы установить глобальную конфигурацию по умолчанию для всех экземпляров сессий, созданных этой библиотекой.
require "resty.session".init({
audience = "my-application",
storage = "redis",
redis = {
username = "session",
password = "storage",
},
})
Смотрите конфигурацию для возможных параметров конфигурации.
Конструкторы
session.new
синтаксис: session = session.new(configuration)
Создает новый экземпляр сессии.
local session = require "resty.session".new()
-- ИЛИ
local session = require "resty.session".new({
audience = "my-application",
})
Смотрите конфигурацию для возможных параметров конфигурации.
Помощники
session.open
синтаксис: session, err, exists = session.open(configuration)
Это можно использовать для открытия сессии, и она либо вернет существующую сессию, либо новую сессию. Параметры возврата exists (логическое значение) указывают, была ли возвращена существующая или новая сессия. Параметр err (строка) содержит сообщение о том, почему открытие могло не удаться (функция все равно вернет session).
local session = require "resty.session".open()
-- ИЛИ
local session, err, exists = require "resty.session".open({
audience = "my-application",
})
Смотрите конфигурацию для возможных параметров конфигурации.
session.start
синтаксис: session, err, exists, refreshed = session.start(configuration)
Это можно использовать для начала сессии, и она либо вернет существующую сессию, либо новую сессию. В случае существующей сессии сессия также будет обновлена (по мере необходимости). Параметр возврата exists (логическое значение) указывает, была ли возвращена существующая или новая сессия. Параметр refreshed (логическое значение) указывает, была ли успешной попытка обновления. Параметр err (строка) содержит сообщение о том, почему открытие или обновление могло не удаться (функция все равно вернет session).
local session = require "resty.session".start()
-- ИЛИ
local session, err, exists, refreshed = require "resty.session".start({
audience = "my-application",
})
Смотрите конфигурацию для возможных параметров конфигурации.
session.logout
синтаксис: ok, err, exists, logged_out = session.logout(configuration)
Выход из конкретной аудитории.
Один cookie сессии может быть разделен между несколькими аудиториями (или приложениями), поэтому необходимо иметь возможность выйти только из одной аудитории, сохраняя сессию для других аудиторий. Параметр возврата exists (логическое значение) указывает, существовала ли сессия. Параметр возврата logged_out (логическое значение) сигнализирует, если сессия существовала и была также завершена. Параметр err (строка) содержит причину, по которой сессия не существовала или почему выход не удался. Параметр ok (истинное значение) будет true, когда сессия существовала и была успешно завершена.
Когда существует только одна аудитория, это можно считать равным session.destroy.
Когда последняя аудитория выходит, cookie также будет уничтожен и недействителен на клиенте.
require "resty.session".logout()
-- ИЛИ
local ok, err, exists, logged_out = require "resty.session".logout({
audience = "my-application",
})
Смотрите конфигурацию для возможных параметров конфигурации.
session.destroy
синтаксис: ok, err, exists, destroyed = session.destroy(configuration)
Уничтожает всю сессию и очищает cookies.
Один cookie сессии может быть разделен между несколькими аудиториями (или приложениями), поэтому необходимо иметь возможность выйти только из одной аудитории, сохраняя сессию для других аудиторий. Параметр возврата exists (логическое значение) указывает, существовала ли сессия. Параметр возврата destroyed (логическое значение) сигнализирует, если сессия существовала и была также уничтожена. Параметр err (строка) содержит причину, по которой сессия не существовала или почему выход не удался. Параметр ok (истинное значение) будет true, когда сессия существовала и была успешно завершена.
require "resty.session".destroy()
-- ИЛИ
local ok, err, exists, destroyed = require "resty.session".destroy({
cookie_name = "auth",
})
Смотрите конфигурацию для возможных параметров конфигурации.
Методы экземпляра
session:open
синтаксис: ok, err = session:open()
Это можно использовать для открытия сессии. Она возвращает true, когда сессия была открыта и проверена. В противном случае она возвращает nil и сообщение об ошибке.
local session = require "resty.session".new()
local ok, err = session:open()
if ok then
-- сессия существует
else
-- сессия не существовала или была недействительна
end
session:save
синтаксис: ok, err = session:save()
Сохраняет данные сессии и выдает новый cookie сессии с новым идентификатором сессии. Когда remember включен, он также выдает новый постоянный cookie и, возможно, сохраняет данные в бэкенд-хранилище. Он возвращает true, когда сессия была сохранена. В противном случае он возвращает nil и сообщение об ошибке.
local session = require "resty.session".new()
session:set_subject("john")
local ok, err = session:save()
if not ok then
-- ошибка при сохранении сессии
end
session:touch
синтаксис: ok, err = session:touch()
Обновляет время бездействия сессии, отправляя обновленный cookie сессии. Он отправляет только клиентский cookie и никогда не вызывает какие-либо API хранилища сессий на бэкенде. Обычно session:refresh используется для косвенного вызова этого. В случае ошибки он возвращает nil и сообщение об ошибке, в противном случае true.
local session, err, exists = require "resty.session".open()
if exists then
ok, err = session:touch()
end
session:refresh
синтаксис: ok, err = session:refresh()
Либо сохраняет сессию (создавая новый идентификатор сессии), либо обновляет сессию в зависимости от того, приближается ли таймаут обновления, что означает, что по умолчанию, когда 3/4 времени обновления истекло, это 45 минут с учетом времени обновления по умолчанию в один час. Обновление имеет порог, по умолчанию одну минуту, поэтому его можно пропустить в некоторых случаях (вы можете вызвать session:touch(), чтобы заставить его сработать). В случае ошибки он возвращает nil и сообщение об ошибке, в противном случае true.
local session, err, exists = require "resty.session".open()
if exists then
local ok, err = session:refresh()
end
Вышеуказанный код выглядит немного как помощник session.start().
session:logout
синтаксис: ok, err = session:logout()
Выход либо уничтожает сессию, либо просто очищает данные для текущей аудитории и сохраняет их (выход из текущей аудитории). В случае ошибки он возвращает nil и сообщение об ошибке, в противном случае true.
local session, err, exists = require "resty.session".open()
if exists then
local ok, err = session:logout()
end
session:destroy
синтаксис: ok, err = session:destroy()
Уничтожает сессию и очищает cookies. В случае ошибки он возвращает nil и сообщение об ошибке, в противном случае true.
local session, err, exists = require "resty.session".open()
if exists then
local ok, err = session:destroy()
end
session:close
синтаксис: session:close()
Просто закрывает экземпляр сессии, чтобы он больше не мог использоваться.
local session = require "resty.session".new()
session:set_subject("john")
local ok, err = session:save()
if not ok then
-- ошибка при сохранении сессии
end
session:close()
session:set_data
синтаксис: session:set_data(data)
Устанавливает данные сессии. data должен быть таблицей.
local session, err, exists = require "resty.session".open()
if not exists then
session:set_data({
cart = {},
})
session:save()
end
session:get_data
синтаксис: data = session:get_data()
Получает данные сессии.
local session, err, exists = require "resty.session".open()
if exists then
local data = session:get_data()
ngx.req.set_header("Authorization", "Bearer " .. data.access_token)
end
session:set
синтаксис: session:set(key, value)
Устанавливает значение в сессии.
local session, err, exists = require "resty.session".open()
if not exists then
session:set("access-token", "eyJ...")
session:save()
end
session:get
синтаксис: value = session:get(key)
Получает значение из сессии.
local session, err, exists = require "resty.session".open()
if exists then
local access_token = session:get("access-token")
ngx.req.set_header("Authorization", "Bearer " .. access_token)
end
session:set_audience
синтаксис: session:set_audience(audience)
Устанавливает аудиторию сессии.
local session = require "resty.session".new()
session.set_audience("my-service")
session:get_audience
синтаксис: audience = session:get_audience()
Устанавливает тему сессии.
session:set_subject
синтаксис: session:set_subject(subject)
Устанавливает аудиторию сессии.
local session = require "resty.session".new()
session.set_subject("[email protected]")
session:get_subject
синтаксис: subject = session:get_subject()
Получает тему сессии.
local session, err, exists = require "resty.session".open()
if exists then
local subject = session.get_subject()
end
session:get_property
синтаксис: value = session:get_property(name)
Получает свойство сессии. Возможные имена свойств:
"id": 43 байта идентификатора сессии (такой же, как nonce, но закодированный в base64 url)"nonce": 32 байта nonce (такой же, как идентификатор сессии, но в необработанных байтах)"audience": Текущая аудитория сессии"subject": Текущая тема сессии"timeout": Ближайший таймаут (в секундах) (что осталось от него)"idling-timeout": Таймаут бездействия сессии (в секундах) (что осталось от него)"rolling-timeout": Таймаут обновления сессии (в секундах) (что осталось от него)"absolute-timeout": Абсолютный таймаут сессии (в секундах) (что осталось от него)
Примечание: возвращаемое значение может быть nil.
local session, err, exists = require "resty.session".open()
if exists then
local timeout = session.get_property("timeout")
end
session:set_remember
синтаксис: session:set_remember(value)
Включает или отключает постоянные сессии.
Во многих формах входа пользователю предоставляется возможность "запомнить меня". Вы можете вызвать эту функцию в зависимости от того, что выбрал пользователь.
local session = require "resty.session".new()
if ngx.var.args.remember then
session:set_remember(true)
end
session:set_subject(ngx.var.args.username)
session:save()
session:get_remember
синтаксис: remember = session:get_remember()
Получает состояние постоянных сессий.
local session, err, exists = require "resty.session".open()
if exists then
local remember = session.get_remember()
end
session:clear_request_cookie
синтаксис: ok, err = session:clear_request_cookie()
Изменяет заголовки запроса, удаляя связанные сессии cookies. Это полезно, когда вы используете библиотеку сессий на прокси-сервере и не хотите, чтобы cookies сессий передавались на upstream-сервис. В случае ошибки он возвращает nil и сообщение об ошибке, в противном случае true (что можно игнорировать).
local session, err, exists = require "resty.session".open()
if exists then
session:clear_request_cookie()
end
session:set_headers
синтаксис: ok, err = session:set_headers(arg1, arg2, ...)
Устанавливает заголовки запроса и ответа на основе конфигурации. В случае ошибки он возвращает nil и сообщение об ошибке, в противном случае true (что можно игнорировать).
local session, err, exists = require "resty.session".open({
request_headers = { "audience", "subject", "id" },
response_headers = { "timeout", "idling-timeout", "rolling-timeout", "absolute-timeout" },
})
if exists then
session:set_headers()
end
Когда вызывается без аргументов, он установит заголовки запроса, настроенные с помощью request_headers, и заголовки ответа, настроенные с помощью response_headers.
Смотрите конфигурацию для возможных имен заголовков.
session:set_request_headers
синтаксис: ok, err = session:set_request_headers(arg1, arg2, ...)
Устанавливает заголовки запроса. В случае ошибки он возвращает nil и сообщение об ошибке, в противном случае true (что можно игнорировать).
local session, err, exists = require "resty.session".open()
if exists then
session:set_request_headers("audience", "subject", "id")
end
Когда вызывается без аргументов, он установит заголовки запроса, настроенные с помощью request_headers.
Смотрите конфигурацию для возможных имен заголовков.
session:set_response_headers
синтаксис: ok, err = session:set_response_headers(arg1, arg2, ...)
Устанавливает заголовки ответа. В случае ошибки он возвращает nil и сообщение об ошибке, в противном случае true (что можно игнорировать).
local session, err, exists = require "resty.session".open()
if exists then
session:set_response_headers("timeout", "idling-timeout", "rolling-timeout", "absolute-timeout")
end
Когда вызывается без аргументов, он установит заголовки ответа, настроенные с помощью response_headers.
Смотрите конфигурацию для возможных имен заголовков.
session.info:set
синтаксис: session.info:set(key, value)
Устанавливает значение в хранилище информации сессии. Хранилище информации сессии может использоваться в сценариях, когда вы хотите хранить данные на стороне сервера, но не хотите создавать новую сессию и отправлять новый cookie сессии. Данные хранилища информации не учитываются при проверке тега аутентификации или кода аутентификации сообщения. Таким образом, если вы хотите использовать это для данных, которые необходимо шифровать, вам нужно зашифровать значение перед передачей его в эту функцию.
local session, err, exists = require "resty.session".open()
if exists then
session.info:set("last-access", ngx.now())
session.info:save()
end
С хранилищем cookie это все еще работает, но тогда это почти то же самое, что и session:set.
session.info:get
синтаксис: value = session.info:get(key)
Получает значение из хранилища информации сессии.
local session, err, exists = require "resty.session".open()
if exists then
local last_access = session.info:get("last-access")
end
session.info:save
синтаксис: ok, err = session.info:save()
Сохраняет информацию. Обновляет только хранилище на бэкенде. Не отправляет новый cookie (кроме хранилища cookie).
local session = require "resty.session".new()
session.info:set("last-access", ngx.now())
local ok, err = session.info:save()
Формат cookie
[ HEADER -------------------------------------------------------------------------------------]
[ Type || Flags || SID || Created at || Rolling Offset || Size || Tag || Idling Offset || Mac ]
[ 1B || 2B || 32B || 5B || 4B || 3B || 16B || 3B || 16B ]
и
[ PAYLOAD --]
[ Data *B ]
Как HEADER, так и PAYLOAD кодируются в base64 url перед помещением в заголовок Set-Cookie. При использовании хранилища на стороне сервера PAYLOAD не помещается в cookie. С хранилищем cookie закодированный в base64 заголовок конкатенируется с закодированным в base64 полезным содержимым.
HEADER имеет фиксированный размер 82 байта в двоичном формате или 110 байт в закодированном в base64 формате.
Поля заголовка объясняются:
- Type: число
1, упакованное в один байт в формате little endian (в настоящее время единственный поддерживаемыйtype). - Flags: упакованные бинарные флаги (short) в двухбайтовом формате little endian.
- SID:
32байта криптографически случайных данных (идентификатор сессии). - Created at: бинарно упакованные секунды от эпохи в формате little endian, усеченные до 5 байт.
- Rolling Offset: бинарно упакованные секунды с момента создания в формате little endian (целое число).
- Size: бинарно упакованный размер данных в трехбайтовом формате little endian.
- Tag:
16байт аутентификационного тега от шифрования AES-256-GCM данных. - Idling Offset: бинарно упакованные секунды с момента создания + rolling offset в формате little endian, усеченные до 3 байт.
- Mac:
16байт кода аутентификации сообщения заголовка.
Шифрование данных
- Начальный ключевой материал (IKM):
- получить IKM из
secret, хешируяsecretс помощью SHA-256, или - использовать 32-байтный IKM, когда он передается в библиотеку с
ikm - Сгенерировать 32 байта криптографически случайного идентификатора сессии (
sid) - Получить 32-байтный ключ шифрования и 12-байтный вектор инициализации с помощью HKDF с использованием SHA-256 (в режиме FIPS используется PBKDF2 с SHA-256 вместо этого)
- Использовать HKDF extract для получения нового ключа из
ikm, чтобы получитьkey(этот шаг можно выполнить только один раз для каждогоikm):- длина вывода:
32 - хеш:
"sha256" - ключ:
<ikm> - режим:
только извлечение - информация:
"" - соль:
""
- длина вывода:
- Использовать HKDF expand для получения
44байтoutput:- длина вывода:
44 - хеш:
"sha256" - ключ:
<key> - режим:
только расширение - информация:
"encryption:<sid>" - соль:
""
- длина вывода:
- Первые 32 байта
outputявляются ключом шифрования (aes-key), а последние 12 байт — вектором инициализации (iv) - Шифровать
plaintext(кодированный в JSON и, возможно, сжатый) с использованием AES-256-GCM, чтобы получитьciphertextиtag - шифр:
"aes-256-gcm" - ключ:
<aes-key> - iv:
<iv> - plaintext:
<plaintext> - aad: используйте первые 47 байт
headerкакaad, которые включают:- Type
- Flags
- Идентификатор сессии
- Время создания
- Rolling Offset
- Размер данных
Существует вариация для cookies remember на шаге 3, где мы можем использовать PBKDF2 вместо HKDF, в зависимости от настройки remember_safety (мы также используем это в режиме FIPS). Настройки PBKDF2:
- длина вывода:
44 - хеш:
"sha256" - пароль:
<key> - соль:
"encryption:<sid>" - итерации:
<1000|10000|100000|1000000> - pkcs5:
1(соответствует FIPS в нашем случае, но необходимо отключить проверки, основанные наSP800-132, такие как количество итераций, см. https://docs.openssl.org/master/man7/provider-kdf/#kdf-parameters)
Количество итераций основано на настройке remember_safety ("Low", "Medium", "High", "Very High"), если remember_safety установлено в "None", мы будем использовать HDKF, как указано выше.
Примечание: Для обратной совместимости мы отключили проверки соответствия SP800-132 в режиме FIPS. Эти проверки гарантируют, что длина соли составляет не менее 128 бит, длина производного ключа составляет не менее 112 бит, а количество итераций составляет не менее 1000. Эти проверки отключены по умолчанию в стандартном поставщике OpenSSL, но включены по умолчанию в поставщике FIPS.
Аутентификация заголовка cookie
- Получите 32-байтный аутентификационный ключ (
mac_key) с помощью HKDF с использованием SHA-256 (в режиме FIPS используется PBKDF2 с SHA-256 вместо этого):- Используйте HKDF extract для получения нового ключа из
ikm, чтобы получитьkey(этот шаг можно выполнить только один раз для каждогоikmи повторно использовать при генерации ключа шифрования):- длина вывода:
32 - хеш:
"sha256" - ключ:
<ikm> - режим:
только извлечение - информация:
"" - соль:
""
- длина вывода:
- Используйте HKDF expand для получения
32байтmac-key:- длина вывода:
32 - хеш:
"sha256" - ключ:
<key> - режим:
только расширение - информация:
"authentication:<sid>" - соль:
""
- длина вывода:
- Используйте HKDF extract для получения нового ключа из
- Рассчитайте код аутентификации сообщения с помощью HMAC-SHA256:
- хеш:
"sha256" - ключ:
<mac-key> - сообщение: используйте первые 66 байт
header, которые включают:- Type
- Flags
- Идентификатор сессии
- Время создания
- Rolling Offset
- Размер данных
- Tag
- Idling Offset
Интерфейс пользовательского хранилища
Если вы хотите реализовать пользовательское хранилище, вам нужно реализовать следующий интерфейс:
---
-- <custom> backend for session library
--
-- @module <custom>
---
-- Storage
-- @section instance
local metatable = {}
metatable.__index = metatable
function metatable.__newindex()
error("attempt to update a read-only table", 2)
end
---
-- Store session data.
--
-- @function instance:set
-- @tparam string name cookie name
-- @tparam string key session key
-- @tparam string value session value
-- @tparam number ttl session ttl
-- @tparam number current_time current time
-- @tparam[opt] string old_key old session id
-- @tparam string stale_ttl stale ttl
-- @tparam[opt] table metadata table of metadata
-- @treturn true|nil ok
-- @treturn string error message
function metatable:set(name, key, value, ttl, current_time, old_key, stale_ttl, metadata)
-- NYI
end
---
-- Retrieve session data.
--
-- @function instance:get
-- @tparam string name cookie name
-- @tparam string key session key
-- @treturn string|nil session data
-- @treturn string error message
function metatable:get(name, key)
-- NYI
end
---
-- Delete session data.
--
-- @function instance:delete
-- @tparam string name cookie name
-- @tparam string key session key
-- @tparam[opt] table metadata session meta data
-- @treturn boolean|nil session data
-- @treturn string error message
function metatable:delete(name, key, current_time, metadata)
-- NYI
end
local storage = {}
---
-- Constructors
-- @section constructors
---
-- Configuration
-- @section configuration
---
-- <custom> storage backend configuration
-- @field <field-name> TBD
-- @table configuration
---
-- Create a <custom> storage.
--
-- This creates a new shared memory storage instance.
--
-- @function module.new
-- @tparam[opt] table configuration <custom> storage @{configuration}
-- @treturn table <custom> storage instance
function storage.new(configuration)
-- NYI
-- return setmetatable({}, metatable)
end
return storage
Пожалуйста, проверьте существующие реализации для деталей. И, пожалуйста, сделайте pull-request, чтобы мы могли интегрировать это непосредственно в библиотеку для других пользователей.
GitHub
Вы можете найти дополнительные советы по конфигурации и документацию для этого модуля в репозитории GitHub для nginx-module-session.