JavaRush /Курси /Модуль 4: FastAPI /Робота з токенами: створення та перевірка валідності

Робота з токенами: створення та перевірка валідності

Модуль 4: FastAPI
Рівень 4 , Лекція 6
Відкрита

По суті, токени — це ключі доступу. Вони дозволяють вашому застосунку зрозуміти: хто ви, які права у вас є, і ще переконатися, що ви не зловмисник. Це особливо важливо для API-запитів, де сервер не зберігає стан між запитами, а про вас знає лише за переданим токеном.

Уявіть, що ви намагаєтеся потрапити в закритий клуб. Без ключа (читай: токена) ви просто залишитесь за дверима. З правильним ключем, але простроченим, вас попросять оновити його (як refresh-токен). Наша задача — зробити цей процес автоматичним, але безпечним.


Пристрій токенів

Згадаймо, як виглядає JWT:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiZXhwIjoxNjY3OTEyMzQ1fQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Він складається з трьох частин:

  1. Header — шапка токена, що вказує на алгоритм підпису (наприклад, HMAC SHA256).
  2. Payload — корисні дані (id користувача, час закінчення, права доступу).
  3. Signature — підпис, щоб ніхто не міг змінити дані в payload.

Детальніше цю структуру ми вже розбирали, тож перейдемо до практики.


Генерація токенів

Для початку нам потрібна бібліотека PyJWT. Якщо ви її ще не встановили, зробіть це:

pip install pyjwt

Нехай у нас є користувач з унікальним user_id. Ми створимо для нього токен, який буде дійсний протягом години.


import jwt
from datetime import datetime, timedelta

# Секретный ключ для подписи токенов (должен быть защищённым!)
SECRET_KEY = "supersecretkey"

def create_access_token(user_id: int):
    # Устанавливаем время истечения (через 1 час)
    expiration = datetime.utcnow() + timedelta(hours=1)

    # Собираем payload
    payload = {
        "sub": user_id,        # Уникальный идентификатор пользователя
        "exp": expiration      # Время истечения токена
    }

    # Генерируем JWT
    token = jwt.encode(payload, SECRET_KEY, algorithm="HS256")
    return token

# Пример использования
token = create_access_token(user_id=42)
print(f"Access token: {token}")

Этот токен можно передавать клиенту. Клиент, в свою очередь, будет отправлять его в заголовке Authorization при запросах к нашему API.

Пара слов о безопасности

Ваш SECRET_KEY — це найцінніше для підпису й перевірки токенів. Якщо він витече, токени можна буде підробити. Зберігайте його в змінній оточення або в спеціальному менеджері секретів (наприклад, Vault).


Перевірка та валідність токенів

Тепер навчимося перевіряти токени, які передаються в запитах. Ми будемо декодувати їх і перевіряти:

  1. Підпис токена.
  2. Термін дії (expiration).

Базовий приклад перевірки токена


def verify_access_token(token: str):
    try:
        # Раскодируем токен
        payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
        return payload  # Возвращаем полезные данные из токена
    except jwt.ExpiredSignatureError:
        raise Exception("Токен истёк")
    except jwt.InvalidTokenError:
        raise Exception("Недействительный токен")

# Пример использования
try:
    payload = verify_access_token(token)
    print(f"Token payload: {payload}")
except Exception as e:
    print(f"Ошибка проверки токена: {e}")

Робота з токенами у FastAPI

Давайте впровадимо все це в застосунок на FastAPI. Ми захистимо ендпоінти за допомогою токенів. Для цього знадобиться:

  1. Генерація токенів при вході користувача.
  2. Перевірка токена для доступу до захищених маршрутів.

Ми створимо ендпоінт /login, який видає токен після перевірки облікових даних користувача.


from fastapi import FastAPI, HTTPException, Depends
from fastapi.security import OAuth2PasswordRequestForm

app = FastAPI()

# Фейковая база данных
users_db = {"user@example.com": {"password": "password123", "id": 1}}

@app.post("/login")
def login(form_data: OAuth2PasswordRequestForm = Depends()):
    user = users_db.get(form_data.username)
    if not user or user["password"] != form_data.password:
        raise HTTPException(status_code=400, detail="Неверные учетные данные")
    
    access_token = create_access_token(user_id=user["id"])
    return {"access_token": access_token, "token_type": "bearer"}

Тепер клієнт надсилає дані облікового запису (логін і пароль) і отримує токен доступу.

Захист ендпоінтів

Щоб захистити API, ми будемо перевіряти токен перед виконанням запиту. FastAPI надає клас OAuth2PasswordBearer, який витягує токен із заголовка Authorization.


from fastapi.security import OAuth2PasswordBearer

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/login")

@app.get("/protected")
def protected_route(token: str = Depends(oauth2_scheme)):
    payload = verify_access_token(token)
    return {"message": f"Привет, пользователь {payload['sub']}!"}

Якщо токен прострочений або недійсний, користувач отримає помилку.


Робота з refresh-токенами

Refresh-токени дозволяють оновлювати токени доступу без необхідності входу в систему. Схема роботи:

  1. Користувач отримує два токени: access_token і refresh_token.
  2. Коли access_token закінчується, клієнт відправляє refresh_token для отримання нового access_token.

Реалізація


def create_refresh_token(user_id: int):
    expiration = datetime.utcnow() + timedelta(days=7)
    payload = {
        "sub": user_id,
        "exp": expiration
    }
    return jwt.encode(payload, SECRET_KEY, algorithm="HS256")

@app.post("/refresh")
def refresh_token(refresh_token: str):
    try:
        payload = verify_access_token(refresh_token)
        new_access_token = create_access_token(user_id=payload["sub"])
        return {"access_token": new_access_token, "token_type": "bearer"}
    except Exception as e:
        raise HTTPException(status_code=401, detail="Неверный refresh токен")

Типові помилки та їх запобігання

  1. Прострочені токени. При перевірці токена можна отримати помилку jwt.ExpiredSignatureError. Це нормальна поведінка, оскільки термін дії токена вже минув. Додайте обробник цієї помилки, щоб клієнт розумів, що треба оновити токен.
  2. Недійсні токени. Якщо токен був підроблений або пошкоджений, бібліотека викидає jwt.InvalidTokenError. Не забудьте акуратно обробляти цю помилку.
  3. Безпека ключів. Ніколи не хардкодьте секретний ключ у коді. Використовуйте змінні оточення.

Такий підхід до роботи з токенами дозволяє будувати надійні та безпечні системи аутентифікації. Закріпіть матеріал, додаючи токени в ваші тестові API і перевіряючи їх на практичних кейсах.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ