Понимание внедрения зависимостей в fastapi

Введение:

В этом посте описывается, как реализовать базовую аутентификацию с использованием fastapi. Если вы новичок в fastapi и ищете краткое введение, прочитайте эту статью.

Аутентификация:

Проще говоря, аутентификация идентифицирует пользователя. Пользователем может быть человек или другое приложение. Для всех конечных точек API, которые вы разрабатываете, нам нужна аутентификация относительно того, кто должен/не должен использовать эти конечные точки. Это цель аутентификации API. Авторизация API — это разрешения, которые вы выбираете для предоставления пользователю для конкретной конечной точки API. Это может быть read-only или read-write и т. д. В основном эти правила устанавливаются с помощью политик или ролей (RBAC) внутри организации.

Fastapi предоставляет несколько механизмов аутентификации, таких как OAuth2, JWT, HTTP Basic Authentication и т. д. В этом посте мы обсудим базовый механизм аутентификации http.

Базовая аутентификация HTTP:

В HTTP Basic Auth приложение ожидает заголовок, содержащий имя пользователя и пароль. Если поля отсутствуют, возникает ошибка HTTP 401 Unauthorised.

from fastapi import Depends, FastAPI
from fastapi.security import HTTPBasic, HTTPBasicCredentials

app = FastAPI()

# Add a basic HTTP authentication
security = HTTPBasic()


@app.get("/users/me")
async def read_current_user(credentials: HTTPBasicCredentials = Depends(security)):
    print(f" Received credentials: {credentials}")
    return {"username": credentials.username, "password": credentials.password}

Примечание. НЕ используйте это в рабочей среде, так как это небезопасно.

FastApi предоставляет сложную систему внедрения зависимостей с помощью параметра Depends. Dependency injection - это просто причудливый термин для предоставления объекту его зависимостей. Это так же хорошо, как передача ваших переменных экземпляра конструктору для инициализации объекта. В нашем случае это предоставление переменных пути для маршрутов API.

В этом случае FastApi предоставляет оболочку или синтаксический сахар через, чтобы обеспечить зависимости для ваших маршрутов. Метод Depends принимает один аргумент, который представляет собой функцию, которая принимает такие аргументы, как ваш путь (параметры запроса, параметры пути и т. д.).

Функция зависит:

  1. Вызов вашей зависимости («надежной») функции с правильными параметрами.
  2. Получите результат от вашей функции.
  3. Назначьте этот результат параметру в вашей функции операции пути.

Теперь, когда мы поняли, как работает Depends, давайте запустим наше приложение fastapi.

(practo-env) dinesh@dinesh practice % uvicorn my_fastapi_auth:app --reload 
INFO:     Will watch for changes in these directories: ['/Users/dkb/Code/practice']
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     Started reloader process [87316] using StatReload
INFO:     Started server process [87318]
INFO:     Waiting for application startup.
INFO:     Application startup complete.

При выполнении этого кода сервер uvicorn запускается и обслуживает порт 8000. Пожалуйста, откройте ссылку http://localhost:8000/users/me в вашем браузере. Запрашивает имя пользователя и пароль.

Проверка:

Теперь давайте попробуем проверить имя пользователя и пароль, добавив код проверки.

from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import HTTPBasic, HTTPBasicCredentials
import secrets

app = FastAPI()

# Add a basic HTTP authentication
security = HTTPBasic()


def validate_credentials(credentials: HTTPBasicCredentials = Depends(security)):

    # encode the credentials to compare
    input_user_name = credentials.username.encode("utf-8")
    input_password = credentials.password.encode("utf-8")

    # DO NOT STORE passwords in plain text. Store them in secure location like vaults or database after encryption.
    # This is just shown for educational purposes
    stored_username = b'dinesh'
    stored_password = b'dinesh'

    is_username = secrets.compare_digest(input_user_name, stored_username)
    is_password = secrets.compare_digest(input_password, stored_password)

    if is_username and is_password:
        return {"auth message": "authentication successful"}

    raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED,
                        detail="Invalid credentials",
                        headers={"WWW-Authenticate": "Basic"})


@app.get("/users/me")
async def read_current_user(username: str = Depends(validate_credentials)):
    return {"message": username}

Сравниваем имя пользователя и пароль с помощью модуля secrets. Это делается для того, чтобы избежать атак по времени. Если вы хотите узнать больше о тайминговых атаках, прочтите этот пост.

Использованная литература:

Первоначально опубликовано на https://dock2learn.com 13 ноября 2022 г.

Дополнительные материалы на PlainEnglish.io. Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter, LinkedIn, YouTube,и Discord. Заинтересованы в Взлом роста? Ознакомьтесь с разделом Схема.