Короткий ответ
JWT состоит из трёх частей — header, payload и signature, закодированных в base64url и разделённых точками; изменение payload без пересчёта подписи корректным секретным ключом приведёт к несовпадению подписи, и сервер должен отклонить такой токен как невалидный.
Как это работает подробнее
Header описывает алгоритм подписи, payload содержит claims (например, user_id, роль, срок действия exp), signature — это HMAC или цифровая подпись от header+payload с использованием секретного (или приватного) ключа. Любой может декодировать base64 и прочитать payload — JWT не шифрует данные, а только подписывает.
Пример кода
example.py
import base64, json
token = "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjo0Mn0.sign_here"
header_b64, payload_b64, signature = token.split(".")
def pad(s):
return s + "=" * (-len(s) % 4)
payload = json.loads(base64.urlsafe_b64decode(pad(payload_b64)))
print(payload) # {'user_id': 42} — читается без секретного ключа и без проверки подписи
# Подмена payload без пересчёта подписи правильным секретом даст сигнатуру,
# не совпадающую при проверке на сервере -> токен отклоняетсяЧто использовать на практике
- Практика. На сервере обязательна проверка подписи и срока действия при каждом запросе; в payload нельзя класть секретные данные (пароли, полные номера карт), так как они читаемы без ключа; важно также не допускать алгоритм «none» в заголовке — это известная уязвимость некоторых библиотек.
Подводные камни
- Риск. Путают подпись с шифрованием и кладут чувствительные данные в payload; не проверяют exp и alg на сервере; хранят JWT в localStorage без понимания риска XSS-кражи токена.