В этой статье речь пойдет о смысле JWT-токена. А о том, как именно его настроить будет в следующей.
Общая картина
- Клиент вводит логин и пароль.
- Сервер проверяет их, и если они верны, высылает в ответе JWT-токен.
- Далее клиент высылает JWT-токен при каждом последующем запросе к серверу в заголовке Authorization
- Сервер проверяет JWT-токен на подлинность.
- И высылает ответ.
JWT токен vs Simple Hash-Based Remember-Me токен
JWT-токен (JSON Web Token) похож на Simple Hash-Based Remember-Me токен, рассмотренный в предыдущей статье (только для использования JWT-токена кода потребуется написать больше):
- Выдается он клиенту после успешного ввода имени и пароля.
- С последующими запросами клиент отправляет его серверу в заголовке, сервер проверяет его подлинность путем простой калькуляции. Удостоверяется что клиент есть тот, за кого себя выдает.
- JWT-токен продолжает работать и после того, как сервер перезапущен.
- JWT-токен работает, если серверов несколько (с балансировщиком нагрузки). На конкретный сервер ничего не завязано: сессий нет, данных, хранящихся в сессии нет.
Есть и отличия:
- В JWT-токене можно хранить больше данных, чем в Simple Hash-Based Remember-Me токене. В JWT-токене есть специальная часть Payload — полезная нагрузка, туда можно записать роли, например. Но можно и обойтись. Обычно достаточно имени пользователя. Ничего секретного в Payload записывать нельзя.
- Чтобы JWT-токен использовать в Spring Security, потребуется написать больше кода, чем с Remember-Me (тут настройка была минимальна).
- Для вычисления подписи в JWT-токене не используется пароль пользователя.
Поскольку сессий нет, статус взаимодействия клиента и сервера хранится на клиенте, а не на сервере (на сервере состояние хранить негде).
Структура JWT
Как показано на официальном сайте (с удобной песочницей), JWT состоит из трех частей, разделенных точкой:
- заголовок (красный)
- полезная нагрузка (фиолетовый)
- подпись (голубой)
Заголовок и полезная нагрузка по сути передаются в открытом виде, хоть и выглядят как непонятные символы. Это формат base64, если вставить символы в любой онлайн-декодер, то получим исходник.
Вот третья часть — хеш, или подпись, как раз и является фишкой, гарантирующей подлинность токена. Из нее не получить «исходник», только наоборот: из исходника подпись можно получить. Что приложение и делает, именно так проверяется подлинность токена.
Как проверяется подлинность токена
Берется заголовок, полезная нагрузка и секретный ключ, из них вычисляется некоторое значение — подпись.
Секретный ключ хранится на стороне сервера. С помощью него подпись вычисляется при первоначальной выдаче токена, и с помощью него же перевычисляется каждый раз, когда приходит токен (иначе говоря, токен проверяется на валидность). Если значение вычисленной подписи совпадает с тем, что в токене, то токен считается валидным. Это принцип поверки, аналогичный тому, что используется с Simple Hash-Based Remember-Me токеном. Только как видите, для JWT не надо даже обращаться к базе и находить пароль пользователя, чтобы вычислить подпись. Пароль в калькуляцию не входит, формула такая:
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), секретный_ключ )
Важно понять, что JWT-токен на сервере не хранится, а просто каждый раз проверяется на подлинность с помощью вышеприведенной формулы.
Хранится только секретный ключ — он един для всех JWT-токенов, выпускаемых приложением.
В каком заголовке передается JWT-токен
Принято передавать его так:
Authorization: Bearer <token>
В следующей статье будет пример приложения с JWT-токеном.
Опечатка:
«Берется заголовок, полезная нагрузка и секретный ключ, их них вычисляется некоторое значение – подпись.»
«… из них ..»
Спасибо за внимательность!