1. Обзор
Веб -токен JSON (JWT) часто используется в безопасности REST API. Несмотря на то, что токен может быть проанализирован такими фреймворками, как Spring Security OAuth , мы можем захотеть обработать токен в нашем собственном коде.
В этом руководстве мы расшифруем и проверим целостность JWT .
2. Структура JWT
Во-первых, давайте разберемся со структурой JWT :
- заголовок
- полезная нагрузка (часто называемая телом)
- подпись
Подпись необязательна. Действительный JWT может состоять только из разделов заголовка и полезной нагрузки. Однако мы используем раздел подписи для проверки содержимого заголовка и полезной нагрузки для авторизации безопасности .
Разделы представлены в виде строк в кодировке base64url, разделенных точкой ('.') в качестве разделителя. По замыслу любой может декодировать JWT и читать содержимое разделов заголовка и полезной нагрузки. Но нам нужен доступ к секретному ключу, используемому для создания подписи, чтобы проверить целостность токена.
Чаще всего JWT содержит «заявки» пользователя. Они представляют данные о пользователе, которые API может использовать для предоставления разрешений или отслеживания пользователя, предоставившего токен. Декодирование маркера позволяет приложению использовать данные, а проверка позволяет приложению доверять тому, что JWT был создан надежным источником.
Давайте посмотрим, как мы можем декодировать и проверять токен в Java.
3. Расшифровка JWT
Мы можем декодировать токен, используя встроенные функции Java.
Во-первых, давайте разделим токен на его разделы:
String[] chunks = token.split("\\.");
Следует отметить, что регулярное выражение, передаваемое в String.split
, использует экранированный символ «.».
символ, чтобы избежать '.' означает «любой символ».
Наш массив кусков
теперь должен иметь два или три элемента, соответствующих разделам JWT.
Затем давайте декодируем части заголовка и полезной нагрузки, используя декодер base64url:
Base64.Decoder decoder = Base64.getUrlDecoder();
String header = new String(decoder.decode(chunks[0]));
String payload = new String(decoder.decode(chunks[1]));
Давайте запустим этот код с помощью JWT (мы можем декодировать онлайн , чтобы сравнить результаты):
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkJhZWxkdW5nIFVzZXIiLCJpYXQiOjE1MTYyMzkwMjJ9.qH7Zj_m3kY69kxhaQXTa-ivIpytKXXjZc1ZSmapZnGE
На выходе мы получим декодированный заголовок любой полезной нагрузки:
{"alg":"HS256","typ":"JWT"}{"sub":"1234567890","name":"ForEach User","iat":1516239022}
Если в JWT определены только разделы заголовка и полезной нагрузки, мы закончили и успешно декодировали информацию.
4. Проверка JWT
Затем мы можем проверить целостность заголовка и полезной нагрузки, чтобы убедиться, что они не были изменены с помощью раздела подписи.
4.1. Зависимости
Для проверки мы можем добавить jjwt в наш pom.xml
:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
Следует отметить, что нам нужна версия этой библиотеки, начиная с версии 0.7.0
.
4.2. Настройка алгоритма подписи и спецификации ключа
Чтобы начать проверку полезной нагрузки и заголовка, нам нужен как алгоритм подписи, который изначально использовался для подписи токена, так и секретный ключ:
SignatureAlgorithm sa = HS256;
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(), sa.getJcaName());
В этом примере мы жестко запрограммировали наш алгоритм подписи на HS256
. Однако мы могли бы расшифровать JSON заголовка и прочитать поле alg
, чтобы получить это значение.
Следует также отметить, что переменная secretKey
является строковым представлением секретного ключа. Мы можем предоставить это нашему приложению через его конфигурацию или через REST API, предоставляемый службой, которая выдает JWT.
4.3. Выполнение проверки
Теперь, когда у нас есть алгоритм подписи и секретный ключ, мы можем приступить к проверке.
Давайте рекомбинируем заголовок и полезную нагрузку в неподписанный JWT, соединив их с помощью '.' разделитель:
String tokenWithoutSignature = chunks[0] + "." + chunks[1];
String signature = chunks[2];
Теперь у нас есть неподписанный токен и предоставленная подпись. Мы можем использовать библиотеку для проверки:
DefaultJwtSignatureValidator validator = new DefaultJwtSignatureValidator(sa, secretKeySpec);
if (!validator.isValid(tokenWithoutSignature, signature)) {
throw new Exception("Could not verify JWT token integrity!");
}
Давайте разберем это.
Сначала мы создаем валидатор с выбранным алгоритмом и секретом. Затем мы предоставляем ему неподписанные данные токена и предоставленную подпись.
Затем валидатор генерирует новую подпись и сравнивает ее с предоставленной подписью. Если они равны, мы проверили целостность заголовка и полезной нагрузки.
5. Вывод
В этой статье мы рассмотрели структуру JWT и способы ее декодирования в JSON.
Затем мы использовали библиотеку для проверки целостности токена, используя его подпись, алгоритм и секретный ключ.
Как всегда, примеры кода из этой статьи можно найти на GitHub .