5.11 Реализация аутентификации с помощью OAuth2 и JWT
7 из 7 шагов пройдено
2 из 2 баллов  получено

Генерация секретного ключа

Прежде чем мы приступим к построению схемы аутентификации, нам сначала необходимо сгенерировать секретный ключ, который является важным элементом создания подписи. JWT имеет заголовок подписи и шифрования объектов JSON (JOSE), который представляет собой метаданные, описывающие, какой алгоритм использовать для кодирования обычного текста, а полезная нагрузка — это данные, которые нам нужно закодировать в токен. Когда клиент запрашивает вход в систему, сервер авторизации подписывает JWT с помощью секретного ключа, который будет сгенерирован с помощью алгоритма. Этот секретный ключ представляет собой закодированную строку, созданную вручную, и ее следует хранить отдельно на сервере авторизации.

openssl — подходящая утилита для генерации этого длинного и рандомного ключа. Запустим следующую команду, чтобы создать ключ:

openssl rand -hex 32

Мы получим следующий результат:

В нашем проекте мы будем хранить секретный ключ и тип алгоритма в переменных. Для этого в файл routers/auth.py добавим следующий код:

SECRET_KEY = 'a21679097c1ba42e9bd06eea239cdc5bf19b249e87698625cba5e3572f005544'
ALGORITHM = 'HS256'

В рамках расширения JWT Python мы выбрали модуль python-jose для генерации токена, поскольку он надежен и имеет дополнительные криптографические функции, которые могут подписывать сложное содержимое данных. Прежде чем использовать этот модуль, сначала установим его с помощью команды pip.

pip install "python-jose[cryptography]"

Продолжим работу с файлом routers/auth.py и добавим следующую функцию:

from datetime import datetime, timedelta
from jose import jwt


async def create_access_token(username: str, user_id: int, is_admin: bool, is_supplier: bool, is_customer: bool, expires_delta: timedelta):
    encode = {'sub': username, 'id': user_id, 'is_admin': is_admin, 'is_supplier': is_supplier, 'is_customer': is_customer}
    expires = datetime.now() + expires_delta
    encode.update({'exp': expires})
    return jwt.encode(encode, SECRET_KEY, algorithm=ALGORITHM)

Итак, конечная точка авторизации будет вызывать метод create_access_token() для запроса JWT. Служба входа предоставляет данные, обычно имя пользователя, которые составляют полезную часть токена. Поскольку JWT должен быть кратковременным, процесс должен обновить часть полезной нагрузки с истекшим сроком действия до некоторого значения даты и времени в минутах или секундах, подходящих для приложения.

Реализация службы login аналогична предыдущей аутентификации OAuth2 на основе пароля, за исключением того, что в этой версии есть вызов create_access_token() для генерации JWT. 

@router.post('/token')
async def login(db: Annotated[AsyncSession, Depends(get_db)], form_data: Annotated[OAuth2PasswordRequestForm, Depends()]):
    user = await authanticate_user(db, form_data.username, form_data.password)

    if not user or user.is_active == False:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail='Could not validate user'
        )

    token = await create_access_token(user.username, user.id, user.is_admin, user.is_supplier, user.is_customer,
                                expires_delta=timedelta(minutes=20))
    return {
        'access_token': token,
        'token_type': 'bearer'
    }

Конечная точка по-прежнему должна возвращать access_token и token_type, поскольку это по-прежнему аутентификация OAuth2 на основе пароля, которая извлекает учетные данные пользователя из OAuth2PasswordRequestForm.

Запустим сервер и проверим работу. Теперь если мы попробуем авторизоваться через конечную точку, мы получим токен для работы.

В следующем шаге мы создадим метод, для доступа через JWT к защищенным конечным точкам.


  • Комментария
Будьте вежливы и соблюдайте наши принципы сообщества. Пожалуйста, не оставляйте решения и подсказки в комментариях, для этого есть отдельный форум.

Я позанудствую еще чутка =)

Нужно добавить в код импорты, без них create_access_token не взлетит

from datetime import datetime, timedelta
from jose import jwt

@Алексей_Бойко, ага, спасибо, добавил.

Доброго дня!

Но подпись будет сгенерирована только с помощью алгоритма, указанного в заголовке, который будет принимать заголовок, полезную нагрузку и секретный ключ в качестве входных данных

Мне кажется здесь ошибка, заголовок не может принимать заголовок, или я не правильно понял =)

@Алексей_Бойко, Спасибо, поправил)