Что такое JWT (JSON Web Token)?

Ответ

JWT (JSON Web Token) — это компактный, URL-безопасный способ представления утверждений (claims), которые должны быть переданы между двумя сторонами. Он часто используется для аутентификации и обмена информацией в веб-приложениях и API.

Структура токена: JWT состоит из трёх частей, разделённых точками и закодированных в Base64Url:

  1. Header (Заголовок): Содержит метаданные — тип токена (typ: "JWT") и алгоритм подписи (alg, например, HS256 или RS256).
  2. Payload (Полезная нагрузка): Содержит утверждения (claims) — данные о пользователе (например, user_id, email) и служебную информацию (время истечения exp, время выдачи iat).
  3. Signature (Подпись): Создаётся путём шифрования подписанных данных (закодированных header и payload) с использованием секретного ключа (для HMAC) или приватного ключа (для RSA). Подпись обеспечивает целостность токена.

Пример создания и проверки JWT в PHP с использованием библиотеки firebase/php-jwt:

use FirebaseJWTJWT;
use FirebaseJWTKey;

// Секретный ключ (для алгоритма HS256)
$secretKey = 'your-secret-key';

// 1. Создание (кодирование) токена
$payload = [
    'iss' => 'your-app.com', // Издатель
    'aud' => 'api.your-app.com', // Аудитория
    'iat' => time(), // Время выдачи
    'exp' => time() + 3600, // Истекает через час
    'user_id' => 123,
    'role' => 'admin'
];

$jwt = JWT::encode($payload, $secretKey, 'HS256');
echo "Созданный токен: $jwtn";

// 2. Проверка и декодирование токена (на стороне API)
try {
    $decoded = JWT::decode($jwt, new Key($secretKey, 'HS256'));
    // $decoded теперь объект с данными из payload
    echo "User ID: " . $decoded->user_id . "n";
    echo "Role: " . $decoded->role . "n";
} catch (Exception $e) {
    // Токен невалиден: просрочен, подпись не совпадает и т.д.
    http_response_code(401);
    echo 'Ошибка аутентификации: ' . $e->getMessage();
}

Типичное использование в HTTP-запросах: Токен передаётся в заголовке Authorization:

Authorization: Bearer <ваш.jwt.токен>

Преимущества и недостатки:

  • Преимущества: Стандартизирован, компактен, самодостаточен (несёт данные), не требует хранения состояния на сервере (stateless).
  • Недостатки: Размер (больше, чем у случайной строки), невозможность немедленного отзыва без дополнительных механизмов (чёрные списки — blacklist), необходимость надёжно хранить секретный ключ.

Безопасность:

  • Никогда не храните конфиденциальные данные (пароли) в payload, так как он легко декодируется.
  • Всегда проверяйте подпись и стандартные claims (exp, iss, aud).
  • Для критичных систем рассмотрите использование короткого времени жизни токена (access token) в паре с механизмом обновления (refresh token).

Ответ 18+ 🔞

А, JWT! Ну, это ж классика, ебать мои старые костыли. Все его используют, а половина нихуя не понимает, как он внутри работает. Сейчас разжуем.

Представь себе бутерброд, но из трёх кусков хлеба, разделённых точками. Первый кусок — это заголовок (Header). Там написано, что это JWT и какой алгоритм подписи использовали, типа HS256. Всё это закодировано в Base64, так что любой желающий может это прочитать — это не секрет.

Второй кусок — это полезная нагрузка (Payload). Вот тут уже интереснее. Ты туда можешь запихнуть что угодно про пользователя: его id, email, роль. И ещё служебную хуйню вроде времени создания (iat) и срока годности (exp). И да, это тоже в открытом виде, просто закодированное. Так что если ты туда пароль запишешь — ты просто пидарас шерстяной, чувак. Все увидят.

А третий кусок — это подпись (Signature). Вот это уже серьёзно. Берут первые два куска (заголовок и нагрузку), склеивают, и этот винегрет шифруют с помощью секретного ключа. Если кто-то попробует в нагрузке role: 'user' поменять на role: 'god', то подпись уже не сойдётся, и токен станет невалидным. Доверия ебать ноль к такому токену.

Вот смотри, как на PHP с библиотекой firebase/php-jwt это выглядит. Создать токен — раз плюнуть.

use FirebaseJWTJWT;
use FirebaseJWTKey;

// Секретный ключ (для алгоритма HS256)
$secretKey = 'your-secret-key';

// 1. Создание (кодирование) токена
$payload = [
    'iss' => 'your-app.com', // Издатель
    'aud' => 'api.your-app.com', // Аудитория
    'iat' => time(), // Время выдачи
    'exp' => time() + 3600, // Истекает через час
    'user_id' => 123,
    'role' => 'admin'
];

$jwt = JWT::encode($payload, $secretKey, 'HS256');
echo "Созданный токен: $jwtn";

А потом на другом конце API ты этот токен проверяешь. Если всё ок — пускаешь в систему. Если нет — посылаешь нахуй с ошибкой 401.

// 2. Проверка и декодирование токена (на стороне API)
try {
    $decoded = JWT::decode($jwt, new Key($secretKey, 'HS256'));
    // $decoded теперь объект с данными из payload
    echo "User ID: " . $decoded->user_id . "n";
    echo "Role: " . $decoded->role . "n";
} catch (Exception $e) {
    // Токен невалиден: просрочен, подпись не совпадает и т.д.
    http_response_code(401);
    echo 'Ошибка аутентификации: ' . $e->getMessage();
}

В HTTP-запросах его обычно тыкают в заголовок Authorization: Bearer <ваш.jwt.токен>. Сервер его вытаскивает и проверяет.

Теперь про плюсы и минусы. Плюсы вроде очевидны: токен самодостаточный, серверу не надо бегать в базу или кеш за сессией каждый раз. Stateless, красота.

Но минусы — это пиздопроебибна. Во-первых, его нельзя отозвать моментально. Выдал токен на час — сиди и молчи, если пользователя забанили, а токен у него ещё живой. Приходится городить чёрные списки (blacklist), а это уже state, и весь смысл теряется. Во-вторых, если секретный ключ утечёт — всё, пизда. Все токены, когда-либо выпущенные, можно считать валидными. Удивление пиздец будет у твоего начальства.

Так что запомни: храни ключ как зеницу ока, делай время жизни токена коротким (минуты, а не дни), а для долгого доступа используй связку Access Token + Refresh Token. И никогда-никогда не клади в payload то, что не должен видеть посторонний. А то будет тебе хитрая жопа с утечкой данных. Всё, лекция окончена.

Видео-ответы