Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Refresh Token — обновление аутентификации
Refresh Token — это специальный токен, который используется для получения нового Access Token без повторной аутентификации пользователя. Это двухуровневый механизм безопасности, часто применяемый в современных API и веб-приложениях.
Зачем нужны два токена?
Access Token:
- Короткоживущий (15 минут - 1 час)
- Используется для каждого запроса к API
- Если скомпрометирован — ущерб ограничен по времени
Refresh Token:
- Долгоживущий (дни, недели, месяцы)
- Хранится безопасно (например, в httpOnly cookie)
- Используется ТОЛЬКО для получения нового Access Token
Поток работы (OAuth 2.0)
1. Пользователь вводит логин/пароль
2. Сервер выдаёт: Access Token + Refresh Token
3. Клиент использует Access Token для каждого запроса
4. Когда Access Token истекает → используем Refresh Token
5. Сервер выдаёт новый Access Token (без повторной аутентификации)
Практическая реализация на Python (FastAPI + JWT)
from fastapi import FastAPI, Depends, HTTPException
from fastapi.security import HTTPBearer
from datetime import datetime, timedelta, UTC
import jwt
from pydantic import BaseModel
app = FastAPI()
security = HTTPBearer()
# Конфигурация
SECRET_KEY = "your-secret-key"
ACCESS_TOKEN_EXPIRE = timedelta(minutes=15)
REFRESH_TOKEN_EXPIRE = timedelta(days=7)
ALGORITHM = "HS256"
class TokenResponse(BaseModel):
access_token: str
refresh_token: str
token_type: str
def create_access_token(user_id: str) -> str:
"""Создание короткоживущего Access Token"""
payload = {
"user_id": user_id,
"exp": datetime.now(UTC) + ACCESS_TOKEN_EXPIRE,
"type": "access"
}
return jwt.encode(payload, SECRET_KEY, algorithm=ALGORITHM)
def create_refresh_token(user_id: str) -> str:
"""Создание долгоживущего Refresh Token"""
payload = {
"user_id": user_id,
"exp": datetime.now(UTC) + REFRESH_TOKEN_EXPIRE,
"type": "refresh"
}
return jwt.encode(payload, SECRET_KEY, algorithm=ALGORITHM)
@app.post("/login")
def login(username: str, password: str) -> TokenResponse:
"""Вход — выдача обоих токенов"""
# Проверка учётных данных
user_id = "user123" # Результат проверки
return TokenResponse(
access_token=create_access_token(user_id),
refresh_token=create_refresh_token(user_id),
token_type="bearer"
)
@app.post("/refresh")
def refresh_token(token: str) -> TokenResponse:
"""Обновление Access Token с помощью Refresh Token"""
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
# Проверяем тип токена
if payload.get("type") != "refresh":
raise HTTPException(status_code=401, detail="Invalid token type")
user_id = payload.get("user_id")
if not user_id:
raise HTTPException(status_code=401, detail="Invalid token")
# Выдаём новый Access Token
return TokenResponse(
access_token=create_access_token(user_id),
refresh_token=token, # Refresh Token остаётся прежним
token_type="bearer"
)
except jwt.ExpiredSignatureError:
raise HTTPException(status_code=401, detail="Refresh token expired")
except jwt.DecodeError:
raise HTTPException(status_code=401, detail="Invalid token")
@app.get("/protected")
def protected_route(token: str = Depends(security)) -> dict:
"""Защищённый эндпойнт — требует валидный Access Token"""
try:
payload = jwt.decode(token.credentials, SECRET_KEY, algorithms=[ALGORITHM])
if payload.get("type") != "access":
raise HTTPException(status_code=401, detail="Invalid token type")
return {"message": f"Hello, user {payload[user_id]}"}
except jwt.ExpiredSignatureError:
raise HTTPException(status_code=401, detail="Access token expired")
except jwt.DecodeError:
raise HTTPException(status_code=401, detail="Invalid token")
Безопасность и best practices
# 1. Хранение в httpOnly cookie (защита от XSS)
from fastapi.responses import JSONResponse
response = JSONResponse({"access_token": access_token})
response.set_cookie(
"refresh_token",
refresh_token,
httponly=True, # Недоступен JavaScript
secure=True, # Только HTTPS
samesite="strict" # Защита от CSRF
)
return response
# 2. Блокирование (revocation) токенов
# Можно использовать Redis для хранения чёрных списков
blacklist = set()
def logout(token: str):
"""Добавить токен в чёрный список"""
blacklist.add(token)
def is_token_blacklisted(token: str) -> bool:
return token in blacklist
# 3. Ротация Refresh Token (для дополнительной безопасности)
# При каждом обновлении выдаём новый Refresh Token
Ключевые моменты
- Access Token — короткоживущий, для каждого запроса
- Refresh Token — долгоживущий, для получения нового Access
- Разделение — уменьшает окно компрометации
- httpOnly cookies — хранение Refresh Token безопаснее localStorage
- Ротация — выдача нового Refresh при обновлении
- Revocation — возможность отозвать токены (logout)