LINUX.ORG.RU

История изменений

Исправление theNamelessOne, (текущая версия) :

Но возникает одно НО. На многих сайтах есть функция «выйти со всех устройств» или даже список сессий и возможность кикнуть любую сессию. Это полезно в случае, если нечаянно залогинился на чужом компьютере или если тебя поломали, но ты успел сменить пароль.

Не только, есть ещё как минимум один момент — если ты обновил какие-то важные данные юзера (например, удалил у него роль admin), в уже существующих токенах данные останутся протухшими.

В голову пришёл ещё компромиссный вариант, что можно гонять короткоживущие токены второго типа, но с расчётом, что они будут постоянно протухать и будет происходить переавторизация по токену первого типа. В этом случае получается, что функция «выйти со всех устройств» работать будет, но с лагом несколько минут/часов.

(Лаг «в несколько часов» — это security nightmare в данном случае. Stateless JWT должны быть короткоживущими).

В данном контексте в терминах JWT «токен второго типа» обычно называют access token, а «токен первого типа» — refresh token. Чтобы сделать это без лага, тебе всё равно понадобится какое-то хранилище (например, in-memory или Redis), в которое тебе придётся лезть на каждый запрос аутентификации.

Например, юзер нажал «выйти со всех устройств»/«выйти со всех устройств, кроме этого». На сервере:

  1. Инвалидируешь в БД все активные refresh-токены (кроме текущего в случае «выйти со всех устройств, кроме этого»).
  2. Пишешь в Redis ID юзера и текущее время, ставишь TTL этой записи равным времени жизни access-токена.
  3. При запросах аутентификации проверяешь для access-токена наличие записи юзер/таймпстамп в Редисе из прошлого пункта. Если эта запись есть, и таймпстамп больше времени создания токена, отклоняешь токен.

Если же ты обновил данные юзера, и тебе нужно просто инвалидировать access-токены (чтобы получить новые/актуальные), то алгоритм тот же, просто пропускаешь первый пункт.

Также ещё в том же Redis хорошо бы хранить blacklist протухших access-токенов, с тем же TTL, равным времени access-токена, это позволит реализовать функцию «выйти с текущего устройства». Да, тебе тоже его надо будет проверять на каждом запросе аутентификации.

Всё это несколько нивелирует преимущества по производительности и I/O, которые ты описал, но: 1) Redis обычно быстрее и легче, чем традиционная RDBMS; 2) данных там будет гораздо меньше, чем в таблице sessions (т.к. мы храним там только протухшие access-токены и записи user-id,current-timestamp, плюс данные там регулярно очищаются за счёт того, что у каждой записи TTL равен времени жизни access-токена); 3) эти данные не так страшно потерять; 4) Redis достаточно просто масштабировать. Так что это всё равно должно быть быстрее, чем хранить сессии в традиционной RDBMS.

Из минусов такого — тебе это надо запилить, придусмотреть все граничные случаи (а они наверняка ещё есть, я написал только про то, что пришло в голову сразу), покрыть тестами. Реализация получается более сложная в итоге. Я бы тебе рекомендовал сначала убедиться, что один запрос в БД для получения сессии на каждый запрос к твоему сервису действительно является узким местом в твоём приложении.

И вот, почитай ещё:

Исправление theNamelessOne, :

Но возникает одно НО. На многих сайтах есть функция «выйти со всех устройств» или даже список сессий и возможность кикнуть любую сессию. Это полезно в случае, если нечаянно залогинился на чужом компьютере или если тебя поломали, но ты успел сменить пароль.

Не только, есть ещё как минимум один момент — если ты обновил какие-то важные данные юзера (например, удалил у него роль admin), то эти обновлённые данные появятся в новом токене для юзера.

В голову пришёл ещё компромиссный вариант, что можно гонять короткоживущие токены второго типа, но с расчётом, что они будут постоянно протухать и будет происходить переавторизация по токену первого типа. В этом случае получается, что функция «выйти со всех устройств» работать будет, но с лагом несколько минут/часов.

(Лаг «в несколько часов» — это security nightmare в данном случае. Stateless JWT должны быть короткоживущими).

В данном контексте в терминах JWT «токен второго типа» обычно называют access token, а «токен первого типа» — refresh token. Чтобы сделать это без лага, тебе всё равно понадобится какое-то хранилище (например, in-memory или Redis), в которое тебе придётся лезть на каждый запрос аутентификации.

Например, юзер нажал «выйти со всех устройств»/«выйти со всех устройств, кроме этого». На сервере:

  1. Инвалидируешь в БД все активные refresh-токены (кроме текущего в случае «выйти со всех устройств, кроме этого»).
  2. Пишешь в Redis ID юзера и текущее время, ставишь TTL этой записи равным времени жизни access-токена.
  3. При запросах аутентификации проверяешь для access-токена наличие записи юзер/таймпстамп в Редисе из прошлого пункта. Если эта запись есть, и таймпстамп больше времени создания токена, отклоняешь токен.

Если же ты обновил данные юзера, и тебе нужно просто инвалидировать access-токены (чтобы получить новые/актуальные), то алгоритм тот же, просто пропускаешь первый пункт.

Также ещё в том же Redis хорошо бы хранить blacklist протухших access-токенов, с тем же TTL, равным времени access-токена, это позволит реализовать функцию «выйти с текущего устройства». Да, тебе тоже его надо будет проверять на каждом запросе аутентификации.

Всё это несколько нивелирует преимущества по производительности и I/O, которые ты описал, но: 1) Redis обычно быстрее и легче, чем традиционная RDBMS; 2) данных там будет гораздо меньше, чем в таблице sessions (т.к. мы храним там только протухшие access-токены и записи user-id,current-timestamp, плюс данные там регулярно очищаются за счёт того, что у каждой записи TTL равен времени жизни access-токена); 3) эти данные не так страшно потерять; 4) Redis достаточно просто масштабировать. Так что это всё равно должно быть быстрее, чем хранить сессии в традиционной RDBMS.

Из минусов такого — тебе это надо запилить, придусмотреть все граничные случаи (а они наверняка ещё есть, я написал только про то, что пришло в голову сразу), покрыть тестами. Реализация получается более сложная в итоге. Я бы тебе рекомендовал сначала убедиться, что один запрос в БД для получения сессии на каждый запрос к твоему сервису действительно является узким местом в твоём приложении.

И вот, почитай ещё:

Исправление theNamelessOne, :

Но возникает одно НО. На многих сайтах есть функция «выйти со всех устройств» или даже список сессий и возможность кикнуть любую сессию. Это полезно в случае, если нечаянно залогинился на чужом компьютере или если тебя поломали, но ты успел сменить пароль.

Не только, есть ещё как минимум один момент — если ты обновил какие-то важные данные юзера (например, удалил у него роль admin), то клиент об этом никак не узнает, пока не получит новый токен.

В голову пришёл ещё компромиссный вариант, что можно гонять короткоживущие токены второго типа, но с расчётом, что они будут постоянно протухать и будет происходить переавторизация по токену первого типа. В этом случае получается, что функция «выйти со всех устройств» работать будет, но с лагом несколько минут/часов.

(Лаг «в несколько часов» — это security nightmare в данном случае. Stateless JWT должны быть короткоживущими).

В данном контексте в терминах JWT «токен второго типа» обычно называют access token, а «токен первого типа» — refresh token. Чтобы сделать это без лага, тебе всё равно понадобится какое-то хранилище (например, in-memory или Redis), в которое тебе придётся лезть на каждый запрос аутентификации.

Например, юзер нажал «выйти со всех устройств»/«выйти со всех устройств, кроме этого». На сервере:

  1. Инвалидируешь в БД все активные refresh-токены (кроме текущего в случае «выйти со всех устройств, кроме этого»).
  2. Пишешь в Redis ID юзера и текущее время, ставишь TTL этой записи равным времени жизни access-токена.
  3. При запросах аутентификации проверяешь для access-токена наличие записи юзер/таймпстамп в Редисе из прошлого пункта. Если эта запись есть, и таймпстамп больше времени создания токена, отклоняешь токен.

Если же ты обновил данные юзера, и тебе нужно просто инвалидировать access-токены (чтобы получить новые/актуальные), то алгоритм тот же, просто пропускаешь первый пункт.

Также ещё в том же Redis хорошо бы хранить blacklist протухших access-токенов, с тем же TTL, равным времени access-токена, это позволит реализовать функцию «выйти с текущего устройства». Да, тебе тоже его надо будет проверять на каждом запросе аутентификации.

Всё это несколько нивелирует преимущества по производительности и I/O, которые ты описал, но: 1) Redis обычно быстрее и легче, чем традиционная RDBMS; 2) данных там будет гораздо меньше, чем в таблице sessions (т.к. мы храним там только протухшие access-токены и записи user-id,current-timestamp, плюс данные там регулярно очищаются за счёт того, что у каждой записи TTL равен времени жизни access-токена); 3) эти данные не так страшно потерять; 4) Redis достаточно просто масштабировать. Так что это всё равно должно быть быстрее, чем хранить сессии в традиционной RDBMS.

Из минусов такого — тебе это надо запилить, придусмотреть все граничные случаи (а они наверняка ещё есть, я написал только про то, что пришло в голову сразу), покрыть тестами. Реализация получается более сложная в итоге. Я бы тебе рекомендовал сначала убедиться, что один запрос в БД для получения сессии на каждый запрос к твоему сервису действительно является узким местом в твоём приложении.

И вот, почитай ещё:

Исходная версия theNamelessOne, :

Но возникает одно НО. На многих сайтах есть функция «выйти со всех устройств» или даже список сессий и возможность кикнуть любую сессию. Это полезно в случае, если нечаянно залогинился на чужом компьютере или если тебя поломали, но ты успел сменить пароль.

Не только, есть ещё как минимум один момент — если ты обновил какие-то важные данные юзера (например, удалил у него роль admin), то клиент об этом никак не узнает, пока не получит новый токен.

В голову пришёл ещё компромиссный вариант, что можно гонять короткоживущие токены второго типа, но с расчётом, что они будут постоянно протухать и будет происходить переавторизация по токену первого типа. В этом случае получается, что функция «выйти со всех устройств» работать будет, но с лагом несколько минут/часов.

В данном контексте в терминах JWT «токен второго типа» обычно называют access token, а «токен первого типа» — refresh token. Чтобы сделать это без лага, тебе всё равно понадобится какое-то хранилище (например, in-memory или Redis), в которое тебе придётся лезть на каждый запрос аутентификации.

Например, юзер нажал «выйти со всех устройств»/«выйти со всех устройств, кроме этого». На сервере:

  1. Инвалидируешь в БД все активные refresh-токены (кроме текущего в случае «выйти со всех устройств, кроме этого»).
  2. Пишешь в Redis ID юзера и текущее время, ставишь TTL этой записи равным времени жизни access-токена.
  3. При запросах аутентификации проверяешь для access-токена наличие записи юзер/таймпстамп в Редисе из прошлого пункта. Если эта запись есть, и таймпстамп больше времени создания токена, отклоняешь токен.

Если же ты обновил данные юзера, и тебе нужно просто инвалидировать access-токены (чтобы получить новые/актуальные), то алгоритм тот же, просто пропускаешь первый пункт.

Также ещё в том же Redis хорошо бы хранить blacklist протухших access-токенов, с тем же TTL, равным времени access-токена, это позволит реализовать функцию «выйти с текущего устройства». Да, тебе тоже его надо будет проверять на каждом запросе аутентификации.

Всё это несколько нивелирует преимущества по производительности и I/O, которые ты описал, но: 1) Redis обычно быстрее и легче, чем традиционная RDBMS; 2) данных там будет гораздо меньше, чем в таблице sessions (т.к. мы храним там только протухшие access-токены и записи user-id,current-timestamp, плюс данные там регулярно очищаются за счёт того, что у каждой записи TTL равен времени жизни access-токена); 3) эти данные не так страшно потерять; 4) Redis достаточно просто масштабировать. Так что это всё равно должно быть быстрее, чем хранить сессии в традиционной RDBMS.

Из минусов такого — тебе это надо запилить, придусмотреть все граничные случаи (а они наверняка ещё есть, я написал только про то, что пришло в голову сразу). Реализация получается более сложная в итоге. Я бы тебе рекомендовал сначала убедиться, что один запрос в БД для получения сессии на каждый запрос к твоему сервису действительно является узким местом в твоём приложении.

И вот, почитай ещё: