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

# Генерация токена доступа
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, который проверяет данные пользователя, генерирует токен доступа и возвращает его:


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. Если переданы правильные данные, вы получите токен доступа.


Защита эндпоинтов

Добавим защиту маршрутов с проверкой токенов. Для этого используем 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, чтобы обновлять токены доступа:


@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"}

Теперь пользователи могут безопасно обновлять свои токены доступа.


Безопасность и улучшения

  • Храните секреты в переменных окружения. Ваш ключ SECRET_KEY не должен быть в коде.
  • Устанавливайте короткий срок жизни токенов доступа. Например, 15-30 минут.
  • Используйте HTTPS. Никогда не передавайте токены по незащищённым соединениям.
  • Добавьте чёрный список для отозванных токенов. Это поможет заблокировать скомпрометированные токены.

Эта система аутентификации и авторизации отлично подходит для большинства API. Используйте её как базу и расширяйте под ваши нужды, добавляя, например, поддержку нескольких типов ролей, интеграцию с фронтендом или использование реальных баз данных. Это реализация реального рабочего кейса защиты API, так что теперь вы готовы к разработке API как профессионал! 🎉

1
Задача
Модуль 4: FastAPI, 4 уровень, 9 лекция
Недоступна
Базовая аутентификация
Базовая аутентификация
1
Задача
Модуль 4: FastAPI, 4 уровень, 9 лекция
Недоступна
Ролевая авторизация
Ролевая авторизация
3
Опрос
Защита эндпоинтов через JWT, 4 уровень, 9 лекция
Недоступен
Защита эндпоинтов через JWT
Защита эндпоинтов через JWT
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ