Безпека - перші кроки¶
🌐 Переклад ШІ та людьми
Цей переклад виконано ШІ під керівництвом людей. 🤝
Можливі помилки через неправильне розуміння початкового змісту або неприродні формулювання тощо. 🤖
Ви можете покращити цей переклад, допомігши нам краще спрямовувати AI LLM.
Уявімо, що ваш backend API працює на певному домені.
А frontend - на іншому домені або в іншому шляху того ж домену (або у мобільному застосунку).
І ви хочете, щоб frontend міг автентифікуватися в backend, використовуючи ім'я користувача та пароль.
Ми можемо використати OAuth2, щоб збудувати це з FastAPI.
Але заощадимо вам час на читання всієї довгої специфікації, щоб знайти лише потрібні дрібниці.
Скористаймося інструментами, які надає FastAPI, щоб обробляти безпеку.
Як це виглядає¶
Спочатку просто запустімо код і подивімося, як він працює, а потім повернемося, щоб розібратися, що відбувається.
Створіть main.py¶
Скопіюйте приклад у файл main.py:
from typing import Annotated
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/items/")
async def read_items(token: Annotated[str, Depends(oauth2_scheme)]):
return {"token": token}
🤓 Other versions and variants
from typing import Annotated
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/items/")
async def read_items(token: Annotated[str, Depends(oauth2_scheme)]):
return {"token": token}
Tip
Prefer to use the Annotated version if possible.
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/items/")
async def read_items(token: str = Depends(oauth2_scheme)):
return {"token": token}
Tip
Prefer to use the Annotated version if possible.
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/items/")
async def read_items(token: str = Depends(oauth2_scheme)):
return {"token": token}
Запустіть¶
Інформація
Пакет python-multipart автоматично встановлюється з FastAPI, коли ви виконуєте команду pip install "fastapi[standard]".
Однак, якщо ви використовуєте команду pip install fastapi, пакет python-multipart за замовчуванням не включено.
Щоб встановити його вручну, переконайтеся, що ви створили віртуальне оточення, активували його, а потім встановили:
$ pip install python-multipart
Це тому, що OAuth2 використовує «form data» для надсилання username та password.
Запустіть приклад:
$ fastapi dev main.py
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
Перевірте¶
Перейдіть до інтерактивної документації: http://127.0.0.1:8000/docs.
Ви побачите щось подібне:

Кнопка Authorize!
У вас уже є нова блискуча кнопка «Authorize».
А ваша операція шляху має маленький замок у правому верхньому куті, на який можна натиснути.
Якщо натиснути її, з’явиться маленька форма авторизації, щоб ввести username і password (та інші необов’язкові поля):

Примітка
Неважливо, що ви введете у форму, поки що це не спрацює. Але ми скоро це доробимо.
Звісно, це не frontend для кінцевих користувачів, але це чудовий інструмент для інтерактивної документації всього вашого API.
Ним може користуватися команда frontend (якою можете бути і ви самі).
Ним можуть користуватися сторонні застосунки та системи.
І ним також можете користуватися ви самі, щоб налагоджувати, перевіряти та тестувати той самий застосунок.
Потік паролю¶
Тепер повернімося трохи назад і розберімося, що це все таке.
password «flow» - це один зі способів («flows»), визначених в OAuth2, для обробки безпеки та автентифікації.
OAuth2 був спроєктований так, щоб backend або API могли бути незалежними від сервера, який автентифікує користувача.
Але в нашому випадку той самий застосунок FastAPI оброблятиме і API, і автентифікацію.
Тож розгляньмо це у спрощеному вигляді:
- Користувач вводить
usernameіpasswordу frontend і натискаєEnter. - Frontend (у браузері користувача) надсилає ці
usernameіpasswordна специфічну URL-адресу нашого API (оголошену якtokenUrl="token"). - API перевіряє ці
usernameіpasswordта повертає «токен» (ми ще нічого з цього не реалізували).- «Токен» - це просто строка з деяким вмістом, який ми можемо пізніше використати, щоб перевірити цього користувача.
- Зазвичай токен налаштований на завершення строку дії через певний час.
- Тож користувачу доведеться знову увійти пізніше.
- І якщо токен викрадуть, ризик менший. Це не як постійний ключ, який працюватиме завжди (у більшості випадків).
- Frontend тимчасово зберігає цей токен десь.
- Користувач клікає у frontend, щоб перейти до іншого розділу вебзастосунку.
- Frontend має отримати ще дані з API.
- Але для того конкретного кінцевого пункту потрібна автентифікація.
- Тож, щоб автентифікуватися в нашому API, він надсилає заголовок
Authorizationзі значеннямBearerплюс токен. - Якщо токен містить
foobar, вміст заголовкаAuthorizationбуде:Bearer foobar.
OAuth2PasswordBearer у FastAPI¶
FastAPI надає кілька інструментів на різних рівнях абстракції, щоб реалізувати ці функції безпеки.
У цьому прикладі ми використаємо OAuth2 з потоком Password, використовуючи токен Bearer. Це робиться за допомогою класу OAuth2PasswordBearer.
Інформація
«Bearer»-токен - не єдиний варіант.
Але це найкращий для нашого сценарію.
І, можливо, найкращий для більшості сценаріїв, якщо ви не експерт з OAuth2 і точно не знаєте, чому інша опція краще відповідає вашим потребам.
У такому разі FastAPI теж надає інструменти, щоб це збудувати.
Коли ми створюємо екземпляр класу OAuth2PasswordBearer, ми передаємо параметр tokenUrl. Цей параметр містить URL, який клієнт (frontend у браузері користувача) використовуватиме, щоб надіслати username і password для отримання токена.
from typing import Annotated
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/items/")
async def read_items(token: Annotated[str, Depends(oauth2_scheme)]):
return {"token": token}
🤓 Other versions and variants
from typing import Annotated
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/items/")
async def read_items(token: Annotated[str, Depends(oauth2_scheme)]):
return {"token": token}
Tip
Prefer to use the Annotated version if possible.
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/items/")
async def read_items(token: str = Depends(oauth2_scheme)):
return {"token": token}
Tip
Prefer to use the Annotated version if possible.
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/items/")
async def read_items(token: str = Depends(oauth2_scheme)):
return {"token": token}
Порада
Тут tokenUrl="token" відноситься до відносної URL-адреси token, яку ми ще не створили. Оскільки це відносна URL-адреса, вона еквівалентна ./token.
Тому, якщо ваш API розміщений на https://example.com/, це буде https://example.com/token. А якщо на https://example.com/api/v1/, тоді це буде https://example.com/api/v1/token.
Використання відносної URL-адреси важливе, щоб ваша програма продовжувала працювати навіть у просунутому сценарії, як-от Behind a Proxy.
Цей параметр не створює той endpoint / операцію шляху, а декларує, що URL /token буде тим, який клієнт має використовувати, щоб отримати токен. Ця інформація використовується в OpenAPI, а потім - у системах інтерактивної документації API.
Незабаром ми також створимо фактичну операцію шляху.
Інформація
Якщо ви дуже строгий «Pythonista», вам може не подобатися стиль імені параметра tokenUrl замість token_url.
Це тому, що використано ту саму назву, що і в специфікації OpenAPI. Тож якщо вам потрібно буде дослідити ці схеми безпеки глибше, ви зможете просто скопіювати та вставити її, щоб знайти більше інформації.
Змінна oauth2_scheme - це екземпляр OAuth2PasswordBearer, але це також і «викликаємий» об’єкт.
Його можна викликати так:
oauth2_scheme(some, parameters)
Тож його можна використовувати з Depends.
Використання¶
Тепер ви можете передати oauth2_scheme як залежність через Depends.
from typing import Annotated
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/items/")
async def read_items(token: Annotated[str, Depends(oauth2_scheme)]):
return {"token": token}
🤓 Other versions and variants
from typing import Annotated
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/items/")
async def read_items(token: Annotated[str, Depends(oauth2_scheme)]):
return {"token": token}
Tip
Prefer to use the Annotated version if possible.
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/items/")
async def read_items(token: str = Depends(oauth2_scheme)):
return {"token": token}
Tip
Prefer to use the Annotated version if possible.
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/items/")
async def read_items(token: str = Depends(oauth2_scheme)):
return {"token": token}
Ця залежність надасть str, який буде присвоєний параметру token функції операції шляху.
FastAPI знатиме, що може використати цю залежність, щоб визначити «схему безпеки» в схемі OpenAPI (і в автоматичній документації API).
Технічні деталі
FastAPI знатиме, що може використати клас OAuth2PasswordBearer (оголошений у залежності), щоб визначити схему безпеки в OpenAPI, тому що він наслідує fastapi.security.oauth2.OAuth2, який своєю чергою наслідує fastapi.security.base.SecurityBase.
Усі утиліти безпеки, які інтегруються з OpenAPI (і автоматичною документацією API), наслідують SecurityBase. Так FastAPI розуміє, як інтегрувати їх в OpenAPI.
Що відбувається¶
Вона шукатиме в запиті заголовок Authorization, перевірить, чи його значення - це Bearer плюс деякий токен, і поверне токен як str.
Якщо заголовка Authorization немає або значення не містить токена Bearer, вона одразу відповість помилкою зі статус-кодом 401 (UNAUTHORIZED).
Вам навіть не потрібно перевіряти, чи існує токен, щоб повернути помилку. Ви можете бути певні: якщо ваша функція виконується, у параметрі токена буде str.
Ви вже можете спробувати це в інтерактивній документації:

Ми ще не перевіряємо дійсність токена, але це вже початок.
Підсумок¶
Отже, лише 3-4 додатковими рядками ви вже маєте деяку примітивну форму безпеки.