mirror of https://github.com/tiangolo/fastapi.git
🌐 Add Russian translation for `docs/ru/docs/tutorial/security/simple-oauth2.md` (#10599)
This commit is contained in:
parent
b667e6ff12
commit
a54e336b22
|
|
@ -0,0 +1,272 @@
|
|||
# Простая авторизация по протоколу OAuth2 с токеном типа Bearer
|
||||
|
||||
Теперь, отталкиваясь от предыдущей главы, добавим недостающие части, чтобы получить безопасную систему.
|
||||
|
||||
## Получение `имени пользователя` и `пароля`
|
||||
|
||||
Для получения `имени пользователя` и `пароля` мы будем использовать утилиты безопасности **FastAPI**.
|
||||
|
||||
Протокол OAuth2 определяет, что при использовании "аутентификации по паролю" (которую мы и используем) клиент/пользователь должен передавать поля `username` и `password` в полях формы.
|
||||
|
||||
В спецификации сказано, что поля должны быть названы именно так. Поэтому `user-name` или `email` работать не будут.
|
||||
|
||||
Но не волнуйтесь, вы можете показать его конечным пользователям во фронтенде в том виде, в котором хотите.
|
||||
|
||||
А ваши модели баз данных могут использовать любые другие имена.
|
||||
|
||||
Но при авторизации согласно спецификации, требуется использовать именно эти имена, что даст нам возможность воспользоваться встроенной системой документации API.
|
||||
|
||||
В спецификации также указано, что `username` и `password` должны передаваться в виде данных формы (так что никакого JSON здесь нет).
|
||||
|
||||
### Oбласть видимости (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 это просто строки.
|
||||
///
|
||||
|
||||
## Код получения `имени пользователя` и `пароля`
|
||||
|
||||
Для решения задачи давайте воспользуемся утилитами, предоставляемыми **FastAPI**.
|
||||
|
||||
### `OAuth2PasswordRequestForm`
|
||||
|
||||
Сначала импортируйте `OAuth2PasswordRequestForm` и затем используйте ее как зависимость с `Depends` в *эндпоинте* `/token`:
|
||||
|
||||
|
||||
{* ../../docs_src/security/tutorial003_an_py310.py hl[4,78] *}
|
||||
|
||||
`OAuth2PasswordRequestForm` - это класс для использования в качестве зависимости для *функции обрабатывающей эндпоинт*, который определяет тело формы со следующими полями:
|
||||
|
||||
* `username`.
|
||||
* `password`.
|
||||
* Необязательное поле `scope` в виде большой строки, состоящей из строк, разделенных пробелами.
|
||||
* Необязательное поле `grant_type`.
|
||||
|
||||
/// tip | Подсказка
|
||||
По спецификации OAuth2 поле `grant_type` является обязательным и содержит фиксированное значение `password`, но `OAuth2PasswordRequestForm` не обеспечивает этого.
|
||||
|
||||
Если вам необходимо использовать `grant_type`, воспользуйтесь `OAuth2PasswordRequestFormStrict` вместо `OAuth2PasswordRequestForm`.
|
||||
///
|
||||
|
||||
* Необязательное поле `client_id` (в нашем примере он не нужен).
|
||||
* Необязательное поле `client_secret` (в нашем примере он не нужен).
|
||||
|
||||
/// info | Дополнительная информация
|
||||
Форма `OAuth2PasswordRequestForm` не является специальным классом для **FastAPI**, как `OAuth2PasswordBearer`.
|
||||
|
||||
`OAuth2PasswordBearer` указывает **FastAPI**, что это схема безопасности. Следовательно, она будет добавлена в OpenAPI.
|
||||
|
||||
Но `OAuth2PasswordRequestForm` - это всего лишь класс зависимости, который вы могли бы написать самостоятельно или вы могли бы объявить параметры `Form` напрямую.
|
||||
|
||||
Но, поскольку это распространённый вариант использования, он предоставляется **FastAPI** напрямую, просто чтобы облегчить задачу.
|
||||
///
|
||||
|
||||
### Использование данных формы
|
||||
|
||||
/// tip | Подсказка
|
||||
В экземпляре зависимого класса `OAuth2PasswordRequestForm` атрибут `scope`, состоящий из одной длинной строки, разделенной пробелами, заменен на атрибут `scopes`, состоящий из списка отдельных строк, каждая из которых соответствует определенному уровню доступа.
|
||||
|
||||
В данном примере мы не используем `scopes`, но если вам это необходимо, то такая функциональность имеется.
|
||||
///
|
||||
|
||||
Теперь получим данные о пользователе из (ненастоящей) базы данных, используя `username` из поля формы.
|
||||
|
||||
Если такого пользователя нет, то мы возвращаем ошибку "неверное имя пользователя или пароль".
|
||||
|
||||
Для ошибки мы используем исключение `HTTPException`:
|
||||
|
||||
{* ../../docs_src/security/tutorial003_an_py310.py hl[3,79:81] *}
|
||||
|
||||
### Проверка пароля
|
||||
|
||||
На данный момент у нас есть данные о пользователе из нашей базы данных, но мы еще не проверили пароль.
|
||||
|
||||
Давайте сначала поместим эти данные в модель Pydantic `UserInDB`.
|
||||
|
||||
Ни в коем случае нельзя сохранять пароли в открытом виде, поэтому мы будем использовать (пока что ненастоящую) систему хеширования паролей.
|
||||
|
||||
Если пароли не совпадают, мы возвращаем ту же ошибку.
|
||||
|
||||
#### Хеширование паролей
|
||||
|
||||
"Хеширование" означает: преобразование некоторого содержимого (в данном случае пароля) в последовательность байтов (просто строку), которая выглядит как тарабарщина.
|
||||
|
||||
Каждый раз, когда вы передаете точно такое же содержимое (точно такой же пароль), вы получаете точно такую же тарабарщину.
|
||||
|
||||
Но преобразовать тарабарщину обратно в пароль невозможно.
|
||||
|
||||
##### Зачем использовать хеширование паролей
|
||||
|
||||
Если ваша база данных будет украдена, то у вора не будет паролей пользователей в открытом виде, только хэши.
|
||||
|
||||
Таким образом, вор не сможет использовать эти же пароли в другой системе (поскольку многие пользователи используют одни и те же пароли повсеместно, это было бы опасно).
|
||||
|
||||
{* ../../docs_src/security/tutorial003_an_py310.py hl[82:85] *}
|
||||
|
||||
#### Про `**user_dict`
|
||||
|
||||
`UserInDB(**user_dict)` означает:
|
||||
|
||||
*Передавать ключи и значения `user_dict` непосредственно в качестве аргументов ключ-значение, что эквивалентно:*
|
||||
|
||||
```Python
|
||||
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` можно найти в [документации к **Дополнительным моделям**](../extra-models.md#about-user_indict){.internal-link target=_blank}.
|
||||
///
|
||||
|
||||
## Возврат токена
|
||||
|
||||
Ответ эндпоинта `token` должен представлять собой объект в формате JSON.
|
||||
|
||||
Он должен иметь `token_type`. В нашем случае, поскольку мы используем токены типа "Bearer", тип токена должен быть "`bearer`".
|
||||
|
||||
И в нем должна быть строка `access_token`, содержащая наш токен доступа.
|
||||
|
||||
В этом простом примере мы нарушим все правила безопасности, и будем считать, что имя пользователя (username) полностью соответствует токену (token)
|
||||
|
||||
/// tip | Подсказка
|
||||
В следующей главе мы рассмотрим реальную защищенную реализацию с хешированием паролей и токенами <abbr title="JSON Web Tokens">JWT</abbr>.
|
||||
|
||||
Но пока давайте остановимся на необходимых нам деталях.
|
||||
///
|
||||
|
||||
{* ../../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-ошибку, если пользователь не существует или неактивен.
|
||||
|
||||
Таким образом, в нашем эндпоинте мы получим пользователя только в том случае, если он существует, правильно аутентифицирован и активен:
|
||||
|
||||
{* ../../docs_src/security/tutorial003_an_py310.py hl[58:66,69:74,94] *}
|
||||
|
||||
/// info | Дополнительная информация
|
||||
Дополнительный заголовок `WWW-Authenticate` со значением `Bearer`, который мы здесь возвращаем, также является частью спецификации.
|
||||
|
||||
Ответ сервера с HTTP-кодом 401 "UNAUTHORIZED" должен также возвращать заголовок `WWW-Authenticate`.
|
||||
|
||||
В случае с bearer-токенами (наш случай) значение этого заголовка должно быть `Bearer`.
|
||||
|
||||
На самом деле этот дополнительный заголовок можно пропустить и все будет работать.
|
||||
|
||||
Но он приведён здесь для соответствия спецификации.
|
||||
|
||||
Кроме того, могут существовать инструменты, которые ожидают его и могут использовать, и это может быть полезно для вас или ваших пользователей сейчас или в будущем.
|
||||
|
||||
В этом и заключается преимущество стандартов...
|
||||
///
|
||||
|
||||
## Посмотим как это работает
|
||||
|
||||
Откроем интерактивную документацию: <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
|
||||
|
||||
### Аутентификация
|
||||
|
||||
Нажмите кнопку "Авторизация".
|
||||
|
||||
Используйте учётные данные:
|
||||
|
||||
Пользователь: `johndoe`
|
||||
|
||||
Пароль: `secret`
|
||||
|
||||
<img src="/img/tutorial/security/image04.png">
|
||||
|
||||
После авторизации в системе вы увидите следующее:
|
||||
|
||||
<img src="/img/tutorial/security/image05.png">
|
||||
|
||||
### Получение собственных пользовательских данных
|
||||
|
||||
Теперь, используя операцию `GET` с путем `/users/me`, вы получите данные пользователя, например:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"username": "johndoe",
|
||||
"email": "johndoe@example.com",
|
||||
"full_name": "John Doe",
|
||||
"disabled": false,
|
||||
"hashed_password": "fakehashedsecret"
|
||||
}
|
||||
```
|
||||
|
||||
<img src="/img/tutorial/security/image06.png">
|
||||
|
||||
Если щелкнуть на значке замка и выйти из системы, а затем попытаться выполнить ту же операцию ещё раз, то будет выдана ошибка HTTP 401:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"detail": "Not authenticated"
|
||||
}
|
||||
```
|
||||
|
||||
### Неактивный пользователь
|
||||
|
||||
Теперь попробуйте пройти аутентификацию с неактивным пользователем:
|
||||
|
||||
Пользователь: `alice`
|
||||
|
||||
Пароль: `secret2`
|
||||
|
||||
И попробуйте использовать операцию `GET` с путем `/users/me`.
|
||||
|
||||
Вы получите ошибку "Inactive user", как тут:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"detail": "Inactive user"
|
||||
}
|
||||
```
|
||||
|
||||
## Резюме
|
||||
|
||||
Теперь у вас есть инструменты для реализации полноценной системы безопасности на основе `имени пользователя` и `пароля` для вашего API.
|
||||
|
||||
Используя эти средства, можно сделать систему безопасности совместимой с любой базой данных, с любым пользователем или моделью данных.
|
||||
|
||||
Единственным недостатком нашей системы является то, что она всё ещё не защищена.
|
||||
|
||||
В следующей главе вы увидите, как использовать библиотеку безопасного хеширования паролей и токены <abbr title="JSON Web Tokens">JWT</abbr>.
|
||||
Loading…
Reference in New Issue