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

Захист API з використанням токенів безпеки

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

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

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

Завдання токенів безпеки:

  1. Переконатися, що запит походить від авторизованого користувача.
  2. Визначити, які дії користувач може виконувати.
  3. Захистити ваші ендпоінти від несанкціонованого доступу.

Вступ до токенів безпеки: JWT

Найпопулярнішим варіантом для роботи з токенами в сучасних API є JSON Web Token (JWT). Це стандарт RFC 7519, який дозволяє безпечно передавати інформацію між сторонами у вигляді JSON-об'єкта.

JWT — це не просто рядок, а структурований об'єкт, який складається з трьох частин:

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

Коли ці три частини об'єднані, вони формують рядок, який виглядає приблизно так:


eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMjM0NTY3ODkwLCJleHAiOjE2ODg4OTk5OTl9.KyJw2aKXgWU1Lp5Jd0xWJoN3sUYOZ0qGqH8GDF8JMoQ

Встановлення необхідних бібліотек

Для роботи з токенами безпеки у FastAPI потрібна бібліотека python-jose для генерації і перевірки JWT. Також знадобиться python-multipart для читання даних форми (використовується при аутентифікації).

Встановимо їх командою:


pip install python-jose python-multipart

Налаштування безпеки у FastAPI

  1. Створення моделі користувача

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

    
    from pydantic import BaseModel
    
    class User(BaseModel):
        username: str
        full_name: str | None = None
        email: str | None = None
        disabled: bool | None = None
    

    Тепер створимо ще одну модель для передачі даних при вході користувача:

    
    class UserInDB(User):
        hashed_password: str
    

    Зверніть увагу на поле hashed_password. Ми не зберігаємо паролі у відкритому вигляді. Зберігання "хешованих" паролів — це безпечна практика.

  2. Налаштування бази даних користувачів

    Для спрощення тимчасово створимо базу користувачів у пам'яті:

    
    db_users = {
        "jsmith": {
            "username": "jsmith",
            "full_name": "John Smith",
            "email": "jsmith@example.com",
            "hashed_password": "fakehashedpassword",
            "disabled": False,
        }
    }
    
    def get_user(db, username: str):
        user = db.get(username)
        if user:
            return UserInDB(**user)
    
  3. Створення функцій для роботи з токенами

    Для створення JWT використаємо бібліотеку jose.

    
    from jose import JWTError, jwt
    from datetime import datetime, timedelta
    
    SECRET_KEY = "secret"  # Використовуйте більш надійний ключ у реальних проєктах
    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
    

    Для перевірки токенів декодуємо JWT і витягуємо дані.

    
    def verify_token(token: str):
        try:
            payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
            username: str = payload.get("sub")
            if username is None:
                raise ValueError("Invalid token")
            return username
        except JWTError:
            raise ValueError("Invalid token")
    
  4. Реалізація аутентифікації

    Тепер створимо ендпоінт, на якому користувач зможе аутентифікуватися й отримати токен.

    
    from fastapi import Depends, FastAPI, HTTPException, status
    from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
    
    app = FastAPI()
    
    oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
    
    def authenticate_user(username: str, password: str):
        user = get_user(db_users, username)
        if not user or user.hashed_password != "fakehashedpassword":  # Використовуйте bcrypt у реальних проєктах
            return False
        return user
    
    @app.post("/token")
    async def login(form_data: OAuth2PasswordRequestForm = Depends()):
        user = authenticate_user(form_data.username, form_data.password)
        if not user:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Incorrect username or password",
                headers={"WWW-Authenticate": "Bearer"},
            )
        access_token = create_access_token(data={"sub": user.username})
        return {"access_token": access_token, "token_type": "bearer"}
    
  5. Захист ендпоінтів з використанням токенів

    Тепер можемо додати захист на ендпоінт, щоб вимагати токен для доступу.

    
    from fastapi import Depends
    
    async def get_current_user(token: str = Depends(oauth2_scheme)):
        try:
            username = verify_token(token)
            user = get_user(db_users, username)
            if user is None:
                raise HTTPException(
                    status_code=status.HTTP_401_UNAUTHORIZED,
                    detail="Could not validate credentials",
                    headers={"WWW-Authenticate": "Bearer"},
                )
            return user
        except ValueError:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Invalid token",
                headers={"WWW-Authenticate": "Bearer"},
            )
    
    @app.get("/users/me")
    async def read_users_me(current_user: User = Depends(get_current_user)):
        return current_user
    

Корисні поради та практичне застосування

Токени безпеки використовуються повсюдно. Наприклад:

  • У e-commerce платформах для захисту особистого кабінету.
  • У корпоративних застосунках для розмежування прав доступу.
  • У мікросервісній архітектурі для авторизації між сервісами.

Зверніть увагу, що:

  1. Ніколи не використовуйте простий текстовий пароль. Використовуйте бібліотеки на кшталт bcrypt або argon2 для хешування.
  2. Токени мають строк дії. Оновлюйте їх за допомогою refresh-токенів.
  3. Не передавайте токени через незахищені з’єднання (використовуйте HTTPS).

Тепер у нас є базова налаштування безпеки FastAPI з токенами. Ми зробили перший крок до того, щоб не тільки створювати, а й захищати наші API.

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