JWT и FastAPI: как студент-системщик перестал бояться веб-авторизации
Вступление
Обычно я копаюсь в системных вызовах Linux, балуюсь с оконным менеджером bspwm или пишу многопоточные штуки на C++. Веб-разработка долгое время казалась мне чем-то вроде чёрной магии: куча декораторов, dependency injection и бесконечные JSON'ы. Но когда потребовалось запилить бэкенд для одного пет-проекта (бота для Twitch, если быть точным), я решил, что пора закрыть этот гештальт. Выбор пал на FastAPI — говорят, быстрый и асинхронный. А где FastAPI, там и вопрос авторизации. В этой статье я хочу рассказать, как разбирался с JWT, и показать, что даже если вы всю жизнь писали на плюсах, разобраться с этим вполне реально.
Теория: что за зверь этот JWT?
Если отбросить маркетинговую шелуху, JWT (JSON Web Token) — это просто хитрая строка, которая позволяет серверу доверять клиенту без хранения состояния (сессий) на своей стороне. Это очень напоминает мне ключи доступа в SSH: ты создаёшь пару ключей, публичный кладёшь на сервер, а приватным подписываешься. В JWT примерно так же, только в роли подписи — секретный ключ на сервере.
Токен состоит из трёх частей, разделённых точками: header.payload.signature.
- Header: говорит, что это JWT и каким алгоритмом подписан (например, HS256).
- Payload: полезная нагрузка — тут лежит ID пользователя, его роль и время жизни токена (exp). В отличие от SSH, эта часть не шифруется, а только кодируется в base64. Поэтому никогда не кладите туда пароли или номера кредиток.
- Signature: самое главное. Сервер берёт header и payload, добавляет свой секретный ключ и прогоняет через HMAC-SHA256. Если злоумышленник попытается поменять user_id в payload, подпись не сойдётся, и сервер сразу пошлёт его лесом.
В реальных проектах обычно используют два токена: Access Token (живёт минут 15-30) и Refresh Token (может жить неделями). Это сделано для того, чтобы даже если Access Token угонят, ущерб был ограничен по времени, а пользователю не приходилось вводить пароль каждый раз.
Практика: собираем всё в коде
Я не люб, когда в туториалах пишут "просто скопируйте эти 100 строк и оно заработает". Мне всегда хочется понять структуру. Поэтому я разбил проект на слои, как в нормальном софте.
1. Конфигурация (app/core/config.py)
Тут всё просто. Я вынес все настройки в переменные окружения, чтобы не светить секретный ключ в репозитории.
2. Работа с токенами (app/core/security.py)
Вот здесь и происходит вся "магия". Я написал две простые функции: одна создаёт токен, другая — проверяет его и достаёт пользователя.
3. Эндпоинт для логина (app/routers/auth.py)
Здесь FastAPI красиво обрабатывает форму логина и возвращает пару токенов. Обратите внимание на OAuth2PasswordRequestForm — это встроенная зависимость, которая сама разбирает application/x-www-form-urlencoded.
4. Защита эндпоинтов (app/core/security.py и app/routers/users.py)
Самая крутая фишка FastAPI — это dependency injection с Depends. Мы создаём функцию get_current_user, которая будет автоматически вызываться перед каждым защищённым эндпоинтом. Она достаёт токен из заголовка Authorization: Bearer <токен>, проверяет его и возвращает данные о пользователе.
Использовать это проще простого:
Заключение
Для человека, привыкшего к fork() и epoll, мир веб-фреймворков поначалу кажется перегруженным абстракциями. Но если копнуть чуть глубже, видишь те же базовые принципы: клиент-сервер, запрос-ответ и необходимость в надёжной аутентификации. FastAPI с его dependency injection и встроенной поддержкой JWT делает этот процесс удивительно простым и понятным. Теперь я могу сосредоточиться на логике своего бота, а не на изобретении велосипеда для авторизации. Надеюсь, мой опыт поможет и вам быстрее вкатиться в эту тему.
P.S. Если вы, как и я, любите копаться в исходниках и системных вызовах, рекомендую всё же попробовать FastAPI. Это хороший пример того, как высокоуровневые абстракции могут экономить время, не скрывая при этом сути происходящего.