Ни один из двух вариантов особенно хорош, так как оба изменят строку таблицы и, следовательно, генерируют ввод-вывод. При 1000 q/s вам нужно лучшее решение. On 2ndQuadrant - это blog post on authenticating users through connection pooling в контексте безопасности на уровне строк. В блоге есть некоторые проблемы ИМХО и не относящиеся к делу материалы, поэтому я постараюсь переделать его здесь правильно (или прочитать мой комментарий к сообщению в блоге).
В Java - как и в большинстве других языков программирования и/или фреймворков - пул соединений является предпочтительным способом подключения к серверу базы данных по соображениям производительности. Существует неявный контракт, который приложение запрашивает экземпляр Connection
из пула, использует его, а затем возвращает экземпляр в пул для некоторого другого потока. Удерживание на Connection
не является вариантом, так как оно прерывает логику объединения. Так действуйте следующим образом:
Подключение объекта бассейн
Создание объекта пула соединений с базой данных кластера учетных данных. Эта роль должна быть GRANT
изменена все необходимые привилегии для таблиц и других объектов.
Authentication
В приложении пользователь проверяет подлинность делает myapp_login(username, password)
или что-то подобное, используя Connection
из пула. В базе данных учетные данные проверяются по таблице users
или независимо от того, что она вызывается в вашей настройке. Если совпадение найдено, создайте случайный токен и вставьте его в таблицу:
CREATE UNLOGGED TABLE sessions (
token text DEFAULT uuid_generate_v4()::text,
login_time timestamp DEFAULT CURRENT_TIME,
user_name integer,
...
);
Добавить столько полей, сколько хотите. Я использую здесь uuid
(приведенный к text
, читайте дальше), но вы также можете указать md5()
или использовать некоторую процедуру pg_crypto
.
Эта таблица должна быть быстрой, так что это UNLOGGED
. Это означает, что он не является аварийно-аварийным и будет усечен после некоторой ошибки сервера, но это не проблема: все сеансы базы данных в любом случае будут признаны недействительными. Кроме того, не ставьте никаких ограничений, как NOT NULL
на таблицу, потому что единственный доступ к этой таблице осуществляется через функции, которые вы как разработчик, обычный пользователь никогда не затрагивает эту таблицу, и каждое ограничение связано с большим количеством циклов процессора.
myapp_login()
функция выглядит примерно так:
CREATE FUNCTION myapp_login(uname text, password text) RETURNS text AS $$
DECLARE
t text;
BEGIN
PERFORM * FROM app_users WHERE username = uname AND pwd = password;
IF FOUND THEN
INSERT INTO sessions(user_name) VALUES (uname) RETURNING token INTO t;
EXECUTE format('SET SESSION "my_app.session_user" TO %s', t);
RETURN t;
END IF;
SET SESSION "my_app.session_user" = '';
RETURN NULL;
END;
$$ LANGUAGE plpgsql STRICT SECURITY DEFINER;
REVOKE EXECUTE ON FUNCTION myapp_login(text, text) FROM PUBLIC;
GRANT EXECUTE ON FUNCTION myapp_login(text, text) TO myapp_role;
Как вы можете видеть, token
также устанавливается в переменной окружения с SET SESSION
(которая нуждается в буквальное значение текста, следовательно uuid::text
гипсе и EXECUTE
команда), а затем возвращается вызывающему. Этот токен сеанса должен храниться где-то в вашем коде приложения на стороне Java.
Функция выполняет поиск по таблице app_users
и INSERT
на столе sessions
. Первое дешево, второе дорого.
Резюме тот же сеанс для дальнейших запросов
Если ваше приложение пользователь нуждается в дальнейшем доступ к базе данных после первых запросов, а затем получить Connection
экземпляр из пула соединений снова, но не называют myapp_ login()
но myapp_resume(token)
вместо , Эта последняя функция ищет маркер в таблице sessions
(дешево) и, если найден, устанавливает переменную сеанса в этот новый токен. Вы также можете проверить, что значение login_time
является последним, или установить его с помощью CURRENT_TIME
, чтобы сохранить сеанс «живым» (дорогим) или сделать любой другой необходимый бизнес.
Хитрость заключается в том, чтобы возобновить сеанс как можно более тонкими, поскольку это может происходить много раз во время сеанса (с точки зрения приложения).
закрыть сеанс
Когда пользователь приложение сделано, сделайте myapp_logout(token)
который удаляет строку из sessions
таблицы, которая соответствует маркеру.
Сессии, которые не закрыты должным образом, не удаляются из sessions
таблицы, но я бы не слишком беспокоиться о that.You может планировать работу, которая выполняется один раз в неделю, чтобы удалить все строки, которые старше 6 часов или около того. Это также позволит вам выяснить, откуда, например, возникает ошибка.
Последнее слово на token
. A uuid
- это просто случайное число, но вы также можете сделать хэш имени пользователя приложения с некоторыми случайными данными и использовать его, например, в RLS или в каком-либо другом режиме доступа на основе строк; сообщение в блоге, которое я ссылаюсь на выше, имеет хорошую информацию об этом. В приложении, которое я разработал, я связываю строку из таблицы users
с тем, что пользователю разрешено видеть. В любом случае вы должны действительно взвесить pro и con: хэш, который может быть использован в RLS, звучит неплохо, но для этого требуется, чтобы хеш был пересчитан (что имеет тенденцию быть дорогим) и сравнивается с хэшем сессии по каждому запросу, повторный поиск по таблице пользователей также является накладными расходами. Установка другой переменной сеанса, которая может быть проверена во время запроса с current_setting()
, может быть хорошей альтернативой.
Простым решением будет использование кварца. Затем вы можете запланировать задание, чтобы запустить время до момента удаления истекших токенов. Кварц также хорошо ладит с весной. – Jodevan
Вариант 2 на самом деле не является решением. Это ваше требование. Итак, какое решение осталось, кроме решения 1? Почему бы вам не поставить дату истечения срока в сам токен. Тогда нет необходимости в базе данных. См. Https://jwt.io/ –
Итак, скажем, до тех пор, пока не начнет запускаться планировщик, в базе данных в нем будет 1000 токенов, в этом случае программе необходимо выполнить поиск данного токена более чем 1000 записей.когда его увеличение до большего количества токенов повлияет на производительность? – Suranga