JavaRush /Курси /Модуль 4: FastAPI /Повна реалізація захисту API з JWT

Повна реалізація захисту API з JWT

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

Сьогодні ми навчимось:

  1. Інтегрувати JWT для повного захисту API.
  2. Реалізовувати аутентифікацію та авторизацію на основі ролей.
  3. Створювати систему управління правами доступу.
  4. Будувати безпечну архітектуру API з кращими практиками.

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

Підготовка додатку

Для початку створимо структуру FastAPI додатку і підключимо потрібні залежності:


pip install fastapi[all] pyjwt passlib

Налаштування моделей користувачів

Наша прикладна база даних буде зберігати інформацію про користувачів, їх паролі та ролі. Для простоти використаємо словник як емуляцію бази даних:


from passlib.context import CryptContext

# "Емуляція" бази даних
users_db = {
    "johndoe": {
        "username": "johndoe",
        "password": "$2b$12$...",
        "role": "admin"
    },
    "janedoe": {
        "username": "janedoe",
        "password": "$2b$12$...",
        "role": "user"
    }
}

# Налаштування хешування паролів
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

def verify_password(plain_password, hashed_password):
    return pwd_context.verify(plain_password, hashed_password)

def get_user(username: str):
    return users_db.get(username)

Примітка: паролі в базі зберігаємо тільки в зашифрованому вигляді, щоб не піддавати користувачів ризику. Не повторюйте помилок юності!

Генерація та перевірка JWT

Створимо утиліти для роботи з токенами. Для цього використаємо бібліотеку PyJWT.


from datetime import datetime, timedelta
from jose import JWTError, jwt

SECRET_KEY = "supersecretkey"  # Замініть на унікальний ключ (і тримайте в секреті!)
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30

# Генерація access token
def create_access_token(data: dict):
    to_encode = data.copy()
    expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt

# Перевірка токена
def verify_token(token: str):
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        return payload
    except JWTError:
        return None

Тепер у нас є базові функції для створення і перевірки токенів. Настав час впровадити їх у наш додаток.


Створення системи аутентифікації

Додамо ендпоінт /login, який перевіряє дані користувача, генерує access token і повертає його:


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

app = FastAPI()

@app.post("/login")
def login(form_data: OAuth2PasswordRequestForm = Depends()):
    user = get_user(form_data.username)
    if not user or not verify_password(form_data.password, user["password"]):
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Невірний логін або пароль"
        )
    # Генеруємо токен з username і роллю
    access_token = create_access_token(data={"sub": user["username"], "role": user["role"]})
    return {"access_token": access_token, "token_type": "bearer"}

Тепер ви можете входити в систему, надсилаючи запити через curl або Postman. Якщо передані правильні дані, ви отримаєте access token.


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

Додамо захист маршрутів з перевіркою токенів. Для цього використовуємо Depends і обробник токенів.


from fastapi.security import OAuth2PasswordBearer

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login")

def get_current_user(token: str = Depends(oauth2_scheme)):
    payload = verify_token(token)
    if payload is None:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Невалідний токен"
        )
    return payload

Тепер можна захистити ендпоінт, додавши залежність:


@app.get("/users/me")
def read_current_user(current_user: dict = Depends(get_current_user)):
    return {"username": current_user["sub"], "role": current_user["role"]}

Спробуйте запросити /users/me з токеном. Якщо токен валідний, ви отримаєте інформацію про користувача.


Додаємо авторизацію на основі ролей

А що як ваш друг Джон хоче обмежити доступ до важливих даних тільки для адміністраторів? Ми можемо легко реалізувати це за допомогою перевірки ролі в JWT.

Додамо декоратор для перевірки прав доступу на основі ролей:


def require_role(required_role: str):
    def decorator(current_user: dict = Depends(get_current_user)):
        if current_user["role"] != required_role:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="Недостатньо прав"
            )
        return current_user
    return Depends(decorator)

Тепер ми можемо захистити маршрути, використовуючи цей декоратор:


@app.get("/admin/data")
def read_admin_data(current_user: dict = require_role("admin")):
    return {"message": "Доступ дозволено лише адміністраторам"}

Якщо користувач з роллю user спробує отримати доступ до /admin/data, він отримає помилку 403 Forbidden.


Система оновлення токенів

Додамо ендпоінт /refresh, щоб оновлювати access token:


@app.post("/refresh")
def refresh_token(token: str = Depends(oauth2_scheme)):
    payload = verify_token(token)
    if payload is None or payload.get("role") is None:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Невалідний токен"
        )
    # Генеруємо новий токен
    new_token = create_access_token(data={"sub": payload["sub"], "role": payload["role"]})
    return {"access_token": new_token, "token_type": "bearer"}

Тепер користувачі можуть безпечно оновлювати свої access token.


Безпека та покращення

  • Зберігайте секрети у змінних оточення. Ваш ключ SECRET_KEY не повинен бути в коді.
  • Встановлюйте короткий термін життя токенів доступу. Наприклад, 15–30 хвилин.
  • Використовуйте HTTPS. Ніколи не передавайте токени по незахищених з'єднаннях.
  • Додайте чорний список для відкликаних токенів. Це допоможе заблокувати скомпрометовані токени.

Ця система аутентифікації та авторизації чудово підходить для більшості API. Використовуйте її як базу і розширюйте під ваші потреби, додаючи, наприклад, підтримку кількох типів ролей, інтеграцію з фронтендом або використання реальних баз даних. Це реалізація реального робочого кейсу захисту API, тож тепер ви готові до розробки API як професіонал! 🎉

3
Опитування
Захист ендпоінтів через JWT, рівень 4, лекція 9
Недоступний
Захист ендпоінтів через JWT
Захист ендпоінтів через JWT
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ