Генерация секретного ключа
Прежде чем мы приступим к построению схемы аутентификации, нам сначала необходимо сгенерировать секретный ключ, который является важным элементом создания подписи. 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@Алексей_Бойко, ага, спасибо, добавил.