JWT — это JSON Web Token, компактный и безопасный способ передачи информации между двумя сторонами в виде токена. Здесь ключевое слово — компактный. JWT — это всего лишь строка, которая содержит информацию (payload), закодированную в формате JSON, с подписью для верификации.
Структура JWT
JWT состоит из трёх частей, разделённых точками:
- Header (заголовок) — информация о типе токена (JWT) и алгоритме подписи, например, HMAC или RSA.
- Payload (полезная нагрузка) — собственно данные, которые вы передаёте: например, идентификатор пользователя, роль, срок действия токена.
- Signature (подпись) — цифровая подпись, используемая для проверки целостности данных.
Пример JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJyb2xlIjoiYWRtaW4iLCJleHBpcnkiOjE2NzI4MTU2MDB9.2bc89a2c5e2c0f3bea865d3c8b4d9e3c8123e5f5
Расшифровав это, мы получим:
- Header (base64):
{ "alg": "HS256", "typ": "JWT" } - Payload (base64):
{ "user_id": 1, "role": "admin", "expiry": 1672815600 } - Signature (hex):
2bc89a2c5e2c0f3bea865d3c8b4d9e3c8123e5f5
Эта структура позволяет вам безопасно передавать данные и проверять их подлинность на стороне сервера.
Почему JWT?
Вы, наверное, задаетесь вопросом: "Почему именно JWT? Почему не сессии или другие формы токенов?" Вот несколько причин:
- JWT самодостаточен: он содержит всю информацию о пользователе, не требуя обращения к базе данных.
- Он легко масштабируется: вы можете использовать его в распределённых архитектурах.
- JWT работает вне коробки с большинством современных технологий.
- Токен подписан, что предотвращает его подделку (если используется хороший секретный ключ).
Генерация JWT в FastAPI
FastAPI — это удивительная штука, и благодаря библиотеке PyJWT работа с JWT становится ещё проще.
Установка библиотеки
Для работы с JWT установим библиотеку PyJWT:
pip install pyjwt
Теперь создадим код для генерации токенов.
Генерация токенов
Вот пример функции для создания JWT с использованием алгоритма HMAC SHA256:
from datetime import datetime, timedelta
from typing import Union
import jwt
# Секретный ключ для подписи токенов (держите его в секрете!)
SECRET_KEY = "super_secret_key_please_change_me"
ALGORITHM = "HS256"
def create_access_token(data: dict, expires_delta: Union[timedelta, None] = None):
"""
Создаёт JWT токен.
:param data: Полезная нагрузка токена (например, идентификатор пользователя).
:param expires_delta: Время жизни токена (опционально).
:return: Строка JWT.
"""
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=15) # токен живёт 15 минут по умолчанию
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
Как это работает?
- Мы добавляем поле
expдля указания срока действия токена. - Полезная нагрузка (
data) дополняется информацией о сроке действия. - Затем мы подписываем токен с использованием секретного ключа.
Пример использования функции:
payload = {"user_id": 1, "role": "admin"}
access_token = create_access_token(data=payload, expires_delta=timedelta(hours=1))
print(access_token)
Вы получите строку вида:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJyb2xlIjoiYWRtaW4iLCJleHBpciI6MTY3MjgxNjAwMH0.T1wGbYFvEc2nOdfAqJ5CkpA0u2V2_q2BdV0Ysx8n3VU
Проверка и декодирование JWT
После того как ваш сервер сгенерировал JWT, нужно уметь его проверять.
Проверка токенов с PyJWT
Для проверки токенов используется функция jwt.decode. Вот пример:
def verify_token(token: str):
"""
Проверяет подлинность токена и возвращает полезную нагрузку.
:param token: JWT токен.
:return: Декодированные данные токена.
"""
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
return payload
except jwt.ExpiredSignatureError:
raise Exception("Токен истёк!") # Обработка ошибки истечения срока действия
except jwt.InvalidTokenError:
raise Exception("Недействительный токен!") # Обработка других ошибок
Как это работает?
jwt.decode()проверяет подпись токена.- Если токен истёк, выбрасывается исключение
ExpiredSignatureError. - Для недействительных токенов выбрасывается
InvalidTokenError.
Пример использования:
token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJyb2xlIjoiYWRtaW4iLCJleHBpciI6MTY3MjgxNjAwMH0.T1wGbYFvEc2nOdfAqJ5CkpA0u2V2_q2BdV0Ysx8n3VU"
try:
payload = verify_token(token)
print(payload)
except Exception as e:
print(str(e))
Если всё нормально, вы увидите полезную нагрузку:
{'user_id': 1, 'role': 'admin', 'exp': 1672816000}
Интеграция в FastAPI
Теперь объединим всё это в FastAPI-приложении.
Пример эндпоинта для создания токена
from fastapi import FastAPI, Depends
from pydantic import BaseModel
app = FastAPI()
class TokenData(BaseModel):
username: str
@app.post("/token")
async def generate_token(data: TokenData):
"""
Создаёт JWT токен для пользователя.
"""
payload = {"username": data.username}
access_token = create_access_token(data=payload)
return {"access_token": access_token, "token_type": "bearer"}
Пример эндпоинта для проверки токена
from fastapi.security import OAuth2PasswordBearer
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/protected")
async def protected_route(token: str = Depends(oauth2_scheme)):
"""
Пример защищённого маршрута.
"""
try:
payload = verify_token(token)
return {"message": f"Welcome, {payload['username']}!"}
except Exception as e:
return {"error": str(e)}
Практическое применение
На практике JWT используются для:
- Защиты эндпоинтов: только пользователи с валидными токенами могут получить доступ.
- Подтверждения подлинности: токен содержит информацию только для одного пользователя.
- Микросервисов: JWT позволяет передавать данные между сервисами.
Однако помните: безопасность прежде всего! Держите секретные ключи в секрете и используйте короткие сроки действия токенов.
Официальная документация PyJWT: https://pyjwt.readthedocs.io/
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ