← Назад к вопросам

Что такое JWT?

1.0 Junior🔥 221 комментариев
#REST API и HTTP#Безопасность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

JWT (JSON Web Token)

JWT — это компактный, стателесс способ представления информации между сторонами в виде JSON объекта. Используется для безопасной передачи данных, особенно для аутентификации и авторизации в веб-приложениях.

Структура JWT

JWT состоит из трех частей, разделенных точками:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Часть 1: Header (Заголовок)

Укодируется в Base64, содержит тип токена и алгоритм подписи:

{
  "alg": "HS256",  // Алгоритм подписи
  "typ": "JWT"    // Тип токена
}

Часть 2: Payload (Полезная нагрузка)

Укодируется в Base64, содержит claims (утверждения) — данные о пользователе:

{
  "sub": "1234567890",     // Subject (ID пользователя)
  "name": "John Doe",      // Имя
  "email": "john@example.com",
  "iat": 1516239022,       // Issued At (когда выдан)
  "exp": 1516242622       // Expiration (когда истекает)
}

Часть 3: Signature (Подпись)

Криптографическая подпись первых двух частей с секретным ключом:

SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Как работает JWT

import jwt
from datetime import datetime, timedelta, timezone

# 1️⃣ СОЗДАНИЕ JWT
def create_jwt_token(user_id: int, email: str, secret_key: str) -> str:
    """Создать JWT токен."""
    payload = {
        'user_id': user_id,
        'email': email,
        'iat': datetime.now(timezone.utc),  # Issued at
        'exp': datetime.now(timezone.utc) + timedelta(hours=24)  # Expires in 24 hours
    }
    
    token = jwt.encode(
        payload,
        secret_key,
        algorithm='HS256'
    )
    
    return token

# Использование
token = create_jwt_token(123, 'john@example.com', 'my-secret-key')
print(token)
# eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMjMsImVtYWlsIjoiam9obkBleGFtcGxlLmNvbSIsImlhdCI6MTY0MTc5MjEwMSwiZXhwIjoxNjQxODc4NTAxfQ...

# 2️⃣ ПРОВЕРКА И ДЕКОДИРОВАНИЕ JWT
def verify_jwt_token(token: str, secret_key: str) -> dict:
    """Проверить и декодировать JWT токен."""
    try:
        payload = jwt.decode(
            token,
            secret_key,
            algorithms=['HS256']
        )
        return payload
    except jwt.ExpiredSignatureError:
        raise ValueError("Токен истек")
    except jwt.InvalidTokenError:
        raise ValueError("Недействительный токен")

# Использование
try:
    payload = verify_jwt_token(token, 'my-secret-key')
    print(payload)  # {'user_id': 123, 'email': 'john@example.com', ...}
except ValueError as e:
    print(f"Ошибка: {e}")

JWT в FastAPI

from fastapi import FastAPI, Depends, HTTPException
from fastapi.security import HTTPBearer, HTTPAuthCredentials
from datetime import datetime, timedelta, timezone
import jwt

app = FastAPI()
security = HTTPBearer()
SECRET_KEY = "your-secret-key"
ALGORITHM = "HS256"

# Модель пользователя
class User:
    def __init__(self, id: int, email: str):
        self.id = id
        self.email = email

def create_access_token(user_id: int, email: str) -> str:
    """Создать access token."""
    payload = {
        'user_id': user_id,
        'email': email,
        'type': 'access',
        'iat': datetime.now(timezone.utc),
        'exp': datetime.now(timezone.utc) + timedelta(hours=1)
    }
    return jwt.encode(payload, SECRET_KEY, algorithm=ALGORITHM)

def create_refresh_token(user_id: int) -> str:
    """Создать refresh token (долгоживущий)."""
    payload = {
        'user_id': user_id,
        'type': 'refresh',
        'iat': datetime.now(timezone.utc),
        'exp': datetime.now(timezone.utc) + timedelta(days=7)
    }
    return jwt.encode(payload, SECRET_KEY, algorithm=ALGORITHM)

def get_current_user(credentials: HTTPAuthCredentials = Depends(security)) -> User:
    """Получить текущего пользователя из JWT."""
    token = credentials.credentials
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        user_id = payload.get('user_id')
        email = payload.get('email')
        if user_id is None:
            raise HTTPException(status_code=401, detail="Invalid token")
        return User(user_id, email)
    except jwt.ExpiredSignatureError:
        raise HTTPException(status_code=401, detail="Token expired")
    except jwt.InvalidTokenError:
        raise HTTPException(status_code=401, detail="Invalid token")

# Эндпоинты
@app.post("/login")
async def login(email: str, password: str):
    """Вход и получение токена."""
    # Проверяем email и пароль
    if email == "john@example.com" and password == "password123":
        access_token = create_access_token(123, email)
        refresh_token = create_refresh_token(123)
        return {
            "access_token": access_token,
            "refresh_token": refresh_token,
            "token_type": "bearer"
        }
    raise HTTPException(status_code=401, detail="Invalid credentials")

@app.get("/me")
async def get_me(user: User = Depends(get_current_user)):
    """Получить информацию о текущем пользователе."""
    return {"id": user.id, "email": user.email}

@app.post("/refresh")
async def refresh_token(credentials: HTTPAuthCredentials = Depends(security)):
    """Обновить access token используя refresh token."""
    token = credentials.credentials
    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')
        email = payload.get('email', f"user_{user_id}@example.com")
        
        new_access_token = create_access_token(user_id, email)
        return {"access_token": new_access_token, "token_type": "bearer"}
    except jwt.ExpiredSignatureError:
        raise HTTPException(status_code=401, detail="Refresh token expired")
    except jwt.InvalidTokenError:
        raise HTTPException(status_code=401, detail="Invalid token")

Плюсы JWT

# 1️⃣ Стателесс — сервер не хранит сессии
user_data = jwt.decode(token, SECRET_KEY)  # Можно восстановить в любой момент

# 2️⃣ Масштабируемость — работает в микросервисах
# Сервис А создает токен
token = create_access_token(123, 'john@example.com')
# Сервис B проверяет токен с тем же secret key

# 3️⃣ Мобильные приложения — передается в заголовке
# Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

# 4️⃣ CORS friendly — не требует cookies

# 5️⃣ Содержит все необходимые данные
payload = jwt.decode(token, SECRET_KEY)
user_id = payload['user_id']
scopes = payload.get('scopes', [])  # Права доступа

Минусы JWT

# 1️⃣ Невозможно отозвать токен до истечения
# Токен выдан на 24 часа, но пользователь сменил пароль
# Старый токен остается действительным

# Решение: использовать blacklist
blacklist = set()  # Множество отозванных токенов

def logout(token: str):
    """Отозвать токен."""
    payload = jwt.decode(token, SECRET_KEY)
    blacklist.add(token)

def get_current_user(token: str):
    if token in blacklist:
        raise HTTPException(status_code=401, detail="Token revoked")
    # ...

# 2️⃣ Токены могут быть украдены (XSS)
# Хранить в localStorage небезопасно
# Решение: httpOnly cookies + CSRF protection

# 3️⃣ Размер токена может быть большим
# Много claims = большой токен
# Решается ограничением claims

# 4️⃣ Токен не может быть обновлен
# Если нужно изменить claims, требуется новый токен

Рекомендуемые практики

import os
from datetime import datetime, timedelta, timezone

class JWTConfig:
    """Конфигурация JWT."""
    SECRET_KEY = os.getenv('JWT_SECRET_KEY')
    ALGORITHM = 'HS256'
    ACCESS_TOKEN_EXPIRE_MINUTES = 15  # Короткоживущий access token
    REFRESH_TOKEN_EXPIRE_DAYS = 7     # Долгоживущий refresh token
    
    @staticmethod
    def create_access_token(data: dict) -> str:
        """Создать access token (15 минут)."""
        to_encode = data.copy()
        expire = datetime.now(timezone.utc) + timedelta(
            minutes=JWTConfig.ACCESS_TOKEN_EXPIRE_MINUTES
        )
        to_encode.update({"exp": expire, "type": "access"})
        return jwt.encode(
            to_encode,
            JWTConfig.SECRET_KEY,
            algorithm=JWTConfig.ALGORITHM
        )
    
    @staticmethod
    def create_refresh_token(user_id: int) -> str:
        """Создать refresh token (7 дней)."""
        to_encode = {'user_id': user_id}
        expire = datetime.now(timezone.utc) + timedelta(
            days=JWTConfig.REFRESH_TOKEN_EXPIRE_DAYS
        )
        to_encode.update({"exp": expire, "type": "refresh"})
        return jwt.encode(
            to_encode,
            JWTConfig.SECRET_KEY,
            algorithm=JWTConfig.ALGORITHM
        )
    
    @staticmethod
    def verify_token(token: str) -> dict:
        """Проверить и декодировать токен."""
        try:
            payload = jwt.decode(
                token,
                JWTConfig.SECRET_KEY,
                algorithms=[JWTConfig.ALGORITHM]
            )
            return payload
        except jwt.ExpiredSignatureError:
            raise ValueError("Token expired")
        except jwt.InvalidTokenError:
            raise ValueError("Invalid token")

# Использование
access_token = JWTConfig.create_access_token({'user_id': 123, 'email': 'john@example.com'})
refresh_token = JWTConfig.create_refresh_token(123)
payload = JWTConfig.verify_token(access_token)

JWT vs Session

СвойствоJWTSession
ХранилищеКлиентСервер
МасштабируемостьХорошаяНужна общая БД
БезопасностьПодписьСессионный ID
ОтзывСложныйПростой
РазмерБольшеМаленький
CORSХорошоТребует cookies

Вывод: JWT — это мощный механизм аутентификации для современных приложений, особенно для API и мобильных приложений. Главное — правильно управлять временем жизни токенов и использовать комбинацию access + refresh токенов.

Что такое JWT? | PrepBro