Files
fastapi/docs/uk/docs/tutorial/security/simple-oauth2.md
2026-01-11 17:44:10 +00:00

15 KiB
Raw Blame History

Простий OAuth2 з Password і Bearer

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

Отримайте username і password

Ми використаємо утиліти безпеки FastAPI, щоб отримати username і password.

OAuth2 визначає, що під час використання «password flow» (який ми використовуємо) клієнт/користувач має надіслати поля username і password як form data.

І специфікація каже, що поля мають називатися саме так. Тож user-name або email не спрацюють.

Але не хвилюйтеся — у frontend ви можете показувати це кінцевим користувачам як завгодно.

І ваші моделі бази даних можуть використовувати будь-які інші назви, які вам потрібні.

Але для операції шляху входу (login) нам потрібно використовувати ці назви, щоб бути сумісними зі специфікацією (і мати змогу, наприклад, використовувати інтегровану систему документації API).

Специфікація також вказує, що username і password мають надсилатися як form data (тобто без JSON).

scope

Специфікація також каже, що клієнт може надіслати ще одне поле форми «scope».

Назва поля форми — scope (в однині), але фактично це довгий рядок зі «scopes», розділеними пробілами.

Кожен «scope» — це просто рядок (без пробілів).

Зазвичай їх використовують, щоб оголошувати конкретні дозволи безпеки, наприклад:

  • users:read або users:write — поширені приклади.
  • instagram_basic використовується Facebook / Instagram.
  • https://www.googleapis.com/auth/drive використовується Google.

/// info | Інформація

В OAuth2 «scope» — це просто рядок, що оголошує конкретний потрібний дозвіл.

Не має значення, чи містить він інші символи на кшталт : або чи є він URL.

Ці деталі залежать від реалізації.

Для OAuth2 це просто рядки.

///

Код для отримання username і password

Тепер використаємо утиліти, які надає FastAPI, щоб це обробити.

OAuth2PasswordRequestForm

Спочатку імпортуйте OAuth2PasswordRequestForm і використайте його як залежність через Depends в операції шляху для /token:

{* ../../docs_src/security/tutorial003_an_py310.py hl[4,78] *}

OAuth2PasswordRequestForm — це клас-залежність, який оголошує form body з:

  • username.
  • password.
  • Необов’язковим полем scope як великим рядком, складеним із рядків, розділених пробілами.
  • Необов’язковим grant_type.

/// tip | Порада

Специфікація OAuth2 фактично вимагає поле grant_type із фіксованим значенням password, але OAuth2PasswordRequestForm цього не примушує.

Якщо вам потрібно це примусово перевіряти, використайте OAuth2PasswordRequestFormStrict замість OAuth2PasswordRequestForm.

///

  • Необов’язковим client_id (у нашому прикладі він не потрібен).
  • Необов’язковим client_secret (у нашому прикладі він не потрібен).

/// info | Інформація

OAuth2PasswordRequestForm — не спеціальний клас FastAPI на кшталт OAuth2PasswordBearer.

OAuth2PasswordBearer дає FastAPI знати, що це схема безпеки. Тому її саме так додають до OpenAPI.

А OAuth2PasswordRequestForm — це просто клас-залежність, який ви могли б написати самі, або могли б оголосити параметри Form напряму.

Але оскільки це поширений випадок використання, FastAPI надає його напряму, щоб спростити роботу.

///

Використайте дані форми

/// tip | Порада

Екземпляр класу-залежності OAuth2PasswordRequestForm не матиме атрибута scope з довгим рядком, розділеним пробілами; натомість він матиме атрибут scopes з фактичним списком рядків для кожного надісланого scope.

Ми не використовуємо scopes у цьому прикладі, але ця функціональність доступна, якщо вам вона знадобиться.

///

Тепер отримайте дані користувача з (фейкової) бази даних, використовуючи username з поля форми.

Якщо такого користувача немає, повертаємо помилку «Incorrect username or password».

Для помилки ми використовуємо виняток HTTPException:

{* ../../docs_src/security/tutorial003_an_py310.py hl[3,79:81] *}

Перевірте пароль

На цьому етапі ми маємо дані користувача з нашої бази даних, але ще не перевірили пароль.

Спочатку помістимо ці дані в Pydantic-модель UserInDB.

Ніколи не зберігайте паролі у відкритому вигляді, тож ми використаємо (фейкову) систему хешування паролів.

Якщо паролі не збігаються, повертаємо ту саму помилку.

Хешування паролів

«Hashing» означає: перетворення певного вмісту (у цьому випадку — пароля) на послідовність байтів (просто рядок), яка виглядає як безглуздий набір символів.

Щоразу, коли ви передаєте рівно той самий вміст (рівно той самий пароль), ви отримуєте рівно той самий «набір символів».

Але ви не можете перетворити цей «набір символів» назад у пароль.

Навіщо використовувати хешування паролів

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

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

{* ../../docs_src/security/tutorial003_an_py310.py hl[82:85] *}

Про **user_dict

UserInDB(**user_dict) означає:

Передайте ключі та значення з user_dict напряму як іменовані аргументи, еквівалентно:

UserInDB(
    username = user_dict["username"],
    email = user_dict["email"],
    full_name = user_dict["full_name"],
    disabled = user_dict["disabled"],
    hashed_password = user_dict["hashed_password"],
)

/// info | Інформація

Щоб отримати повніше пояснення **user_dict, поверніться до документації про Додаткові моделі{.internal-link target=_blank}.

///

Поверніть токен

Відповідь endpoint token має бути JSON-об’єктом.

Вона має містити token_type. У нашому випадку, оскільки ми використовуємо токени «Bearer», тип токена має бути bearer.

Також вона має містити access_token — рядок, що містить наш токен доступу.

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

/// tip | Порада

У наступному розділі ви побачите справді безпечну реалізацію з хешуванням паролів і токенами JWT.

Але зараз зосередьмося на конкретних деталях, які нам потрібні.

///

{* ../../docs_src/security/tutorial003_an_py310.py hl[87] *}

/// tip | Порада

За специфікацією, ви повинні повертати JSON з access_token і token_type, як у цьому прикладі.

Це те, що ви маєте зробити самостійно у своєму коді, і переконатися, що використовуєте саме ці ключі JSON.

Це майже єдине, що вам потрібно пам’ятати й зробити правильно самостійно, щоб відповідати специфікаціям.

Усе інше FastAPI обробляє за вас.

///

Оновіть залежності

Тепер ми оновимо наші залежності.

Ми хочемо отримувати current_user лише якщо цей користувач активний.

Тож ми створюємо додаткову залежність get_current_active_user, яка, своєю чергою, використовує get_current_user як залежність.

Обидві ці залежності просто повернуть HTTP-помилку, якщо користувача не існує або якщо він неактивний.

Тож у нашому endpoint ми отримаємо користувача, лише якщо користувач існує, був коректно автентифікований і є активним:

{* ../../docs_src/security/tutorial003_an_py310.py hl[58:66,69:74,94] *}

/// info | Інформація

Додатковий заголовок WWW-Authenticate зі значенням Bearer, який ми тут повертаємо, також є частиною специфікації.

Будь-який HTTP-код (помилки) 401 «UNAUTHORIZED» також має повертати заголовок WWW-Authenticate.

У випадку bearer-токенів (наш випадок) значення цього заголовка має бути Bearer.

Насправді ви можете пропустити цей додатковий заголовок, і все одно все працюватиме.

Але тут його наведено, щоб відповідати специфікаціям.

Також можуть існувати інструменти, які очікують і використовують його (зараз або в майбутньому), і це може бути корисно вам або вашим користувачам зараз або в майбутньому.

У цьому й користь стандартів...

///

Подивіться, як це працює

Відкрийте інтерактивну документацію: http://127.0.0.1:8000/docs.

Автентифікуйтеся

Натисніть кнопку «Authorize».

Використайте облікові дані:

Користувач: johndoe

Пароль: secret

Після автентифікації в системі ви побачите це так:

Отримайте дані свого користувача

Тепер використайте операцію GET зі шляхом /users/me.

Ви отримаєте дані вашого користувача, наприклад:

{
  "username": "johndoe",
  "email": "johndoe@example.com",
  "full_name": "John Doe",
  "disabled": false,
  "hashed_password": "fakehashedsecret"
}

Якщо ви натиснете на іконку замка й вийдете (logout), а потім спробуєте ту саму операцію знову, ви отримаєте HTTP 401 помилку:

{
  "detail": "Not authenticated"
}

Неактивний користувач

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

Користувач: alice

Пароль: secret2

І спробуйте використати операцію GET зі шляхом /users/me.

Ви отримаєте помилку «Inactive user», наприклад:

{
  "detail": "Inactive user"
}

Підсумок

Тепер у вас є інструменти, щоб реалізувати повну систему безпеки на основі username і password для вашого API.

Використовуючи ці інструменти, ви можете зробити систему безпеки сумісною з будь-якою базою даних і будь-якою моделлю користувачів або даних.

Єдина відсутня деталь — вона ще не є по-справжньому «безпечною».

У наступному розділі ви побачите, як використовувати безпечну бібліотеку хешування паролів і токени JWT.