Короткий ответ
Чисто stateless JWT (проверяемый только по подписи, без обращения к хранилищу) плохо совместим с требованием немедленного отзыва, так как токен остаётся валидным до истечения exp, независимо от действий на сервере; session-based подход или JWT с централизованной проверкой отзыва (blacklist/короткий TTL с refresh) решает эту проблему за счёт частичного отказа от stateless-преимуществ — выбор зависит от того, насколько критично немедленное отключение доступа против стоимости дополнительного состояния и задержки от централизованной проверки.
Как это работает подробнее
stateless JWT проверяется локально ресурсным сервером по подписи без сетевого вызова — это даёт низкую латентность и горизонтальную масштабируемость без общего хранилища сессий, но означает, что отозвать конкретный токен до истечения срока действия невозможно без дополнительного механизма (например, списка отозванных токенов, который сам по себе требует централизованного, проверяемого на каждый запрос хранилища — что сводит на нет чистую stateless-природу). Session-based подход хранит состояние на сервере (в Redis/БД) и проверяет его при каждом запросе, что даёт немедленный отзыв (удалить запись сессии), но требует сетевого вызова к общему хранилищу на каждый запрос и делает систему зависимой от доступности этого хранилища.
Пример кода
# Компромиссный вариант: короткий access-токен (JWT) + централизованная проверка отзыва refresh-токена
from datetime import datetime, timedelta, timezone
def issue_tokens(user_id: int, private_key: str):
now = datetime.now(timezone.utc)
access_token = jwt.encode(
{"sub": user_id, "exp": now + timedelta(minutes=5)}, # короткое окно уязвимости
private_key, algorithm="RS256",
)
refresh_token = create_refresh_token(user_id) # хранится в БД, может быть немедленно отозван
return access_token, refresh_token
def revoke_user_access(user_id: int):
revoke_all_refresh_tokens(user_id) # немедленный эффект: новые access-токены не выдать
# существующие access-токены останутся валидны до истечения exp (до 5 минут) — осознанный trade-offЧто использовать на практике
- Рекомендация. распространённый компромисс — короткоживущий access-токен (JWT, проверяется локально, минуты) в сочетании с более долгоживущим refresh-токеном, отзыв которого проверяется централизованно; это ограничивает "окно уязвимости" после компрометации временем жизни access-токена, не требуя централизованной проверки на каждый обычный запрос. Выбор конкретного TTL access-токена — это прямой trade-off между задержкой реакции на компрометацию и частотой обращений к центральному хранилищу для обновления токена. Компромисс: полностью stateless JWT даёт максимальную производительность и простоту масштабирования, но жертвует возможностью немедленного отзыва — приемлемо, если токены короткоживущие и риск от компрометации в течение этого короткого окна приемлем для бизнеса. Session-based (или JWT с централизованной проверкой отзыва) даёт контроль и немедленный отзыв, но добавляет сетевую зависимость от общего хранилища на каждый запрос, точку отказа и дополнительную инфраструктуру. Критерий выбора — чувствительность данных/операций, защищаемых API, требования регуляторов или контрактов безопасности к скорости отзыва доступа, и готовность системы нести издержки централизованной проверки состояния на каждый запрос.
Подводные камни
- Риски. выдавать JWT с длительным сроком действия (часы/дни) без механизма отзыва, что делает компрометацию токена опасной на весь этот срок независимо от действий администратора; реализовывать "отзыв" JWT через проверку централизованного blacklist на каждый запрос, но при этом рекламировать решение как "stateless", не признавая, что оно уже несёт основные издержки session-based подхода (сетевой вызов на каждый запрос).