➡️ Зачем нужны токены?
Если в нашей системе есть REST API, доступный только аутентифицированным пользователям, то при каждом запросе серверу нужно знать, что это наш пользователь. Для этого в каждый запрос прикладывается токен, выпущенный для этого пользователя.
Ранее я писала пост про аутентификацию и авторизацию, и там приводила аналогию с доступом в бизнес-центр.
Продолжу эту аналогию:
Представьте: вы пришли в бизнес-центр.
На входе вы показали паспорт — это логин + пароль
Охранник выдал вам пропуск — это access token
В течение дня вы:
- заходите в офисы
- пользуетесь лифтом
При этом паспорт не показываете, а просто прикладываете пропуск (access-токен).
➡️ Как устроен процесс?
В микросервисной архитектуре за аутентификацию и выдачу токенов обычно отвечает отдельный auth-service:
1️⃣ Пользователь вводит логин и пароль на сайте
2️⃣ Логин и пароль отправляются по https в auth-service
3️⃣ Auth-service проверяет корректность и отправляет в ответ пару: Access Token и Refresh Token. Эти токены хранятся на клиенте (в браузере/мобильном приложении).
4️⃣ Теперь в каждый запрос на бэк добавляется access-токен. Он размещается в заголовке http запроса Authorization:
Authorization: Bearer <токен>
Bearer — это название схемы аутентификации. Бывают разные, например Basic (логин и пароль передаются).
5️⃣ Бэкенд-сервис проверяет валидность токена (срок, подпись и др.). Если все ок, на основании данных из токена определяет пользователя, проверяет его права и обслуживает запрос.
Это код обычно вынесен в служебный класс, чтобы не мешать бизнес-логике.
➡️ Что такое JWT
Access-токен обычно имеет формат JWT.
JWT (JSON Web Token) — это строка, состоящая из трёх частей, разделённых точками
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.KMUFsIDTnFmyG3nMiGM6H9FNFUROf3wh7SmqJp-QV30
🟢 Заголовок — закодирован Base64Url и содержит JSON c типом токена и алгоритмом, который использовался для подписи токена:
{
"alg": "HS256",
"typ": "JWT"
}Base64Url— это кодировка из символов A–Z, a–z, 0–9, - и _, безопасная для использования в URL и HTTP-заголовках.
🟢 Payload — закодирован Base64Url и содержит JSON c обязательными и дополнительными полями. Эти поля называют claims.
Например, стандартные claims:
sub (subject) - идентификатор субъекта
iat (issued at) - время выпуска
exp (expiration time) - момент времени, после которого токен считается недействительным
{
"sub": "1234567890",
"iat": 1516239022,
"roles": ["admin"]
}При генерации токена в него можно добавить свои поля, например, roles.
‼️ Содержимое токена не шифруется, поэтому в нем нельзя передавать пароли или другие чувствительные данные.
Обычно в токене передается только id пользователя, а если бэку нужны, например, ФИО этого пользователя, то микросервис бэка, получивший запрос, отдельно идет за ФИО в auth или другой сервис, хранящий данные пользователя.
🌟 Роли (права) в токене
В токене могут быть указаны роли пользователя, а могут и нет, смотря как реализована система. Если ролей нет, то бэкенд-сервис, получив из токена userId, обращается с ним в сервис прав (auth-service или отдельный), получает список прав и проверяет доступ.
Есть еще редкий кейс: если нужно иметь возможность мгновенно изменить роль пользователя, то указание ролей в JWT не подходит, так как роли в уже выданном токене изменить нельзя. Все 15 минут жизни JWT там будут те же роли, что при выдаче.
🟢 Подпись — криптографическая подпись полей header и payload, которая позволяет проверить, что токен не был подделан
Посмотреть содержимое токена и проверить валидность подписи вручную можно на сайте jwt.io
Продолжение ⬇️