committed by
GitHub
1 changed files with 232 additions and 0 deletions
@ -0,0 +1,232 @@ |
|||||
|
# Безопасность - первые шаги |
||||
|
|
||||
|
Представим, что у вас есть свой **бэкенд** API на некотором домене. |
||||
|
|
||||
|
И у вас есть **фронтенд** на другом домене или на другом пути того же домена (или в мобильном приложении). |
||||
|
|
||||
|
И вы хотите иметь возможность аутентификации фронтенда с бэкендом, используя **имя пользователя** и **пароль**. |
||||
|
|
||||
|
Мы можем использовать **OAuth2** для создания такой системы с помощью **FastAPI**. |
||||
|
|
||||
|
Но давайте избавим вас от необходимости читать всю длинную спецификацию, чтобы найти те небольшие кусочки информации, которые вам нужны. |
||||
|
|
||||
|
Для работы с безопасностью воспользуемся средствами, предоставленными **FastAPI**. |
||||
|
|
||||
|
## Как это выглядит |
||||
|
|
||||
|
Давайте сначала просто воспользуемся кодом и посмотрим, как он работает, а затем детально разберём, что происходит. |
||||
|
|
||||
|
## Создание `main.py` |
||||
|
|
||||
|
Скопируйте пример в файл `main.py`: |
||||
|
|
||||
|
=== "Python 3.9+" |
||||
|
|
||||
|
```Python |
||||
|
{!> ../../../docs_src/security/tutorial001_an_py39.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+" |
||||
|
|
||||
|
```Python |
||||
|
{!> ../../../docs_src/security/tutorial001_an.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+ без Annotated" |
||||
|
|
||||
|
!!! tip "Подсказка" |
||||
|
Предпочтительнее использовать версию с аннотацией, если это возможно. |
||||
|
|
||||
|
```Python |
||||
|
{!> ../../../docs_src/security/tutorial001.py!} |
||||
|
``` |
||||
|
|
||||
|
|
||||
|
## Запуск |
||||
|
|
||||
|
!!! info "Дополнительная информация" |
||||
|
Вначале, установите библиотеку <a href="https://andrew-d.github.io/python-multipart/" class="external-link" target="_blank">`python-multipart`</a>. |
||||
|
|
||||
|
А именно: `pip install python-multipart`. |
||||
|
|
||||
|
Это связано с тем, что **OAuth2** использует "данные формы" для передачи `имени пользователя` и `пароля`. |
||||
|
|
||||
|
Запустите ваш сервер: |
||||
|
|
||||
|
<div class="termy"> |
||||
|
|
||||
|
```console |
||||
|
$ uvicorn main:app --reload |
||||
|
|
||||
|
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) |
||||
|
``` |
||||
|
|
||||
|
</div> |
||||
|
|
||||
|
## Проверка |
||||
|
|
||||
|
Перейдите к интерактивной документации по адресу: <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>. |
||||
|
|
||||
|
Вы увидите примерно следующее: |
||||
|
|
||||
|
<img src="/img/tutorial/security/image01.png"> |
||||
|
|
||||
|
!!! check "Кнопка авторизации!" |
||||
|
У вас уже появилась новая кнопка "Authorize". |
||||
|
|
||||
|
А у *операции пути* теперь появился маленький замочек в правом верхнем углу, на который можно нажать. |
||||
|
|
||||
|
При нажатии на нее появляется небольшая форма авторизации, в которую нужно ввести `имя пользователя` и `пароль` (и другие необязательные поля): |
||||
|
|
||||
|
<img src="/img/tutorial/security/image02.png"> |
||||
|
|
||||
|
!!! note "Технические детали" |
||||
|
Неважно, что вы введете в форму, она пока не будет работать. Но мы к этому еще придем. |
||||
|
|
||||
|
Конечно, это не фронтенд для конечных пользователей, но это отличный автоматический инструмент для интерактивного документирования всех ваших API. |
||||
|
|
||||
|
Он может использоваться командой фронтенда (которой можете быть и вы сами). |
||||
|
|
||||
|
Он может быть использован сторонними приложениями и системами. |
||||
|
|
||||
|
Кроме того, его можно использовать самостоятельно для отладки, проверки и тестирования одного и того же приложения. |
||||
|
|
||||
|
## Аутентификация по паролю |
||||
|
|
||||
|
Теперь давайте вернемся немного назад и разберемся, что же это такое. |
||||
|
|
||||
|
Аутентификация по паролю является одним из способов, определенных в OAuth2, для обеспечения безопасности и аутентификации. |
||||
|
|
||||
|
OAuth2 был разработан для того, чтобы бэкэнд или API были независимы от сервера, который аутентифицирует пользователя. |
||||
|
|
||||
|
Но в нашем случае одно и то же приложение **FastAPI** будет работать с API и аутентификацией. |
||||
|
|
||||
|
Итак, рассмотрим его с этой упрощенной точки зрения: |
||||
|
|
||||
|
* Пользователь вводит на фронтенде `имя пользователя` и `пароль` и нажимает `Enter`. |
||||
|
* Фронтенд (работающий в браузере пользователя) отправляет эти `имя пользователя` и `пароль` на определенный URL в нашем API (объявленный с помощью параметра `tokenUrl="token"`). |
||||
|
* API проверяет эти `имя пользователя` и `пароль` и выдает в ответ "токен" (мы еще не реализовали ничего из этого). |
||||
|
* "Токен" - это просто строка с некоторым содержимым, которое мы можем использовать позже для верификации пользователя. |
||||
|
* Обычно срок действия токена истекает через некоторое время. |
||||
|
* Таким образом, пользователю придется снова войти в систему в какой-то момент времени. |
||||
|
* И если токен будет украден, то риск будет меньше, так как он не похож на постоянный ключ, который будет работать вечно (в большинстве случаев). |
||||
|
* Фронтенд временно хранит этот токен в каком-то месте. |
||||
|
* Пользователь щелкает мышью на фронтенде, чтобы перейти в другой раздел на фронтенде. |
||||
|
* Фронтенду необходимо получить дополнительные данные из API. |
||||
|
* Но для этого необходима аутентификация для конкретной конечной точки. |
||||
|
* Поэтому для аутентификации в нашем API он посылает заголовок `Authorization` со значением `Bearer` плюс сам токен. |
||||
|
* Если токен содержит `foobar`, то содержание заголовка `Authorization` будет таким: `Bearer foobar`. |
||||
|
|
||||
|
## Класс `OAuth2PasswordBearer` в **FastAPI** |
||||
|
|
||||
|
**FastAPI** предоставляет несколько средств на разных уровнях абстракции для реализации этих функций безопасности. |
||||
|
|
||||
|
В данном примере мы будем использовать **OAuth2**, с аутентификацией по паролю, используя токен **Bearer**. Для этого мы используем класс `OAuth2PasswordBearer`. |
||||
|
|
||||
|
!!! info "Дополнительная информация" |
||||
|
Токен "bearer" - не единственный вариант, но для нашего случая он является наилучшим. |
||||
|
|
||||
|
И это может быть лучшим вариантом для большинства случаев использования, если только вы не являетесь экспертом в области OAuth2 и точно знаете, почему вам лучше подходит какой-то другой вариант. |
||||
|
|
||||
|
В этом случае **FastAPI** также предоставляет инструменты для его реализации. |
||||
|
|
||||
|
При создании экземпляра класса `OAuth2PasswordBearer` мы передаем в него параметр `tokenUrl`. Этот параметр содержит URL, который клиент (фронтенд, работающий в браузере пользователя) будет использовать для отправки `имени пользователя` и `пароля` с целью получения токена. |
||||
|
|
||||
|
=== "Python 3.9+" |
||||
|
|
||||
|
```Python hl_lines="8" |
||||
|
{!> ../../../docs_src/security/tutorial001_an_py39.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+" |
||||
|
|
||||
|
```Python hl_lines="7" |
||||
|
{!> ../../../docs_src/security/tutorial001_an.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+ без Annotated" |
||||
|
|
||||
|
!!! tip "Подсказка" |
||||
|
Предпочтительнее использовать версию с аннотацией, если это возможно. |
||||
|
|
||||
|
```Python hl_lines="6" |
||||
|
{!> ../../../docs_src/security/tutorial001.py!} |
||||
|
``` |
||||
|
|
||||
|
!!! tip "Подсказка" |
||||
|
Здесь `tokenUrl="token"` ссылается на относительный URL `token`, который мы еще не создали. Поскольку это относительный URL, он эквивалентен `./token`. |
||||
|
|
||||
|
Поскольку мы используем относительный URL, если ваш API расположен по адресу `https://example.com/`, то он будет ссылаться на `https://example.com/token`. Если же ваш API расположен по адресу `https://example.com/api/v1/`, то он будет ссылаться на `https://example.com/api/v1/token`. |
||||
|
|
||||
|
Использование относительного URL важно для того, чтобы ваше приложение продолжало работать даже в таких сложных случаях, как оно находится [за прокси-сервером](../../advanced/behind-a-proxy.md){.internal-link target=_blank}. |
||||
|
|
||||
|
Этот параметр не создает конечную точку / *операцию пути*, а объявляет, что URL `/token` будет таким, который клиент должен использовать для получения токена. Эта информация используется в OpenAPI, а затем в интерактивных системах документации API. |
||||
|
|
||||
|
Вскоре мы создадим и саму операцию пути. |
||||
|
|
||||
|
!!! info "Дополнительная информация" |
||||
|
Если вы очень строгий "питонист", то вам может не понравиться стиль названия параметра `tokenUrl` вместо `token_url`. |
||||
|
|
||||
|
Это связано с тем, что тут используется то же имя, что и в спецификации OpenAPI. Таким образом, если вам необходимо более подробно изучить какую-либо из этих схем безопасности, вы можете просто использовать копирование/вставку, чтобы найти дополнительную информацию о ней. |
||||
|
|
||||
|
Переменная `oauth2_scheme` является экземпляром `OAuth2PasswordBearer`, но она также является "вызываемой". |
||||
|
|
||||
|
Ее можно вызвать следующим образом: |
||||
|
|
||||
|
```Python |
||||
|
oauth2_scheme(some, parameters) |
||||
|
``` |
||||
|
|
||||
|
Поэтому ее можно использовать вместе с `Depends`. |
||||
|
|
||||
|
### Использование |
||||
|
|
||||
|
Теперь вы можете передать ваш `oauth2_scheme` в зависимость с помощью `Depends`. |
||||
|
|
||||
|
=== "Python 3.9+" |
||||
|
|
||||
|
```Python hl_lines="12" |
||||
|
{!> ../../../docs_src/security/tutorial001_an_py39.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+" |
||||
|
|
||||
|
```Python hl_lines="11" |
||||
|
{!> ../../../docs_src/security/tutorial001_an.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+ без Annotated" |
||||
|
|
||||
|
!!! tip "Подсказка" |
||||
|
Предпочтительнее использовать версию с аннотацией, если это возможно. |
||||
|
|
||||
|
```Python hl_lines="10" |
||||
|
{!> ../../../docs_src/security/tutorial001.py!} |
||||
|
``` |
||||
|
|
||||
|
Эта зависимость будет предоставлять `строку`, которая присваивается параметру `token` в *функции операции пути*. |
||||
|
|
||||
|
**FastAPI** будет знать, что он может использовать эту зависимость для определения "схемы безопасности" в схеме OpenAPI (и автоматической документации по API). |
||||
|
|
||||
|
!!! info "Технические детали" |
||||
|
**FastAPI** будет знать, что он может использовать класс `OAuth2PasswordBearer` (объявленный в зависимости) для определения схемы безопасности в OpenAPI, поскольку он наследуется от `fastapi.security.oauth2.OAuth2`, который, в свою очередь, наследуется от `fastapi.security.base.SecurityBase`. |
||||
|
|
||||
|
Все утилиты безопасности, интегрируемые в OpenAPI (и автоматическая документация по API), наследуются от `SecurityBase`, поэтому **FastAPI** может знать, как интегрировать их в OpenAPI. |
||||
|
|
||||
|
## Что он делает |
||||
|
|
||||
|
Он будет искать в запросе заголовок `Authorization` и проверять, содержит ли он значение `Bearer` с некоторым токеном, и возвращать токен в виде `строки`. |
||||
|
|
||||
|
Если он не видит заголовка `Authorization` или значение не имеет токена `Bearer`, то в ответ будет выдана ошибка с кодом состояния 401 (`UNAUTHORIZED`). |
||||
|
|
||||
|
Для возврата ошибки даже не нужно проверять, существует ли токен. Вы можете быть уверены, что если ваша функция будет выполнилась, то в этом токене есть `строка`. |
||||
|
|
||||
|
Проверить это можно уже сейчас в интерактивной документации: |
||||
|
|
||||
|
<img src="/img/tutorial/security/image03.png"> |
||||
|
|
||||
|
Мы пока не проверяем валидность токена, но для начала неплохо. |
||||
|
|
||||
|
## Резюме |
||||
|
|
||||
|
Таким образом, всего за 3-4 дополнительные строки вы получаете некую примитивную форму защиты. |
Loading…
Reference in new issue