Быстрый старт
От установки до проверенного токена за пять минут: вы выпустите подписанный JWT для вошедшего пользователя, вернёте его при следующем запросе и проверите, прежде чем довериться хотя бы одному claim.
Что вы построите
Небольшой поток «вход, затем доступ». При входе вы выпускаете токен, который сообщает, кто этот пользователь и когда токен истекает. При следующем запросе клиент отправляет его обратно, и вы проверяете его — подпись и срок действия — прежде чем прочитать идентификатор пользователя. Вот весь цикл: выпустить → отправить → проверить → прочитать.
Мы будем использовать HS256 (общий секрет), потому что для него не нужны файлы
ключей. Переход на RSA или ECDSA позже меняет только ключ, но не поток — см.
«Асимметричные ключи».
Прежде чем начать
- PHP ≥ 8.1 с
ext-opensslиext-json(проверьте черезphp -m) - Надёжный секрет (см. «Установка и требования»)
Установите пакет:
composer require flytachi/jwtШаг 1 — Выпустите токен при входе
Соберите JwtPayload с нужными вам claims, затем подпишите его с помощью PrivateKey.
<?php
require 'vendor/autoload.php';
use Flytachi\Jwt\JWT;
use Flytachi\Jwt\Entity\JwtPayload;
use Flytachi\Jwt\Entity\PrivateKey;
$secret = getenv('JWT_SECRET'); // загрузите его, никогда не прописывайте в коде
$now = time();
$token = JWT::encode(
new JwtPayload([
'iss' => 'https://my-app.com', // кто его выпустил
'sub' => 'user-42', // о ком он
'iat' => $now, // время выпуска
'exp' => $now + 3600, // истекает через один час
]),
new PrivateKey($secret, 'HS256')
);
echo $token; // eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...Что это за claims?
iss, sub, iat, exp — это стандартные claims. exp и iat (а также nbf)
проверяются автоматически при декодировании. Вы также можете добавить любой
собственный claim — см. «Работа с claims».
Шаг 2 — Отправьте его обратно и проверьте
При следующем запросе клиент возвращает токен (обычно в заголовке
Authorization: Bearer <token>). Проверьте его с помощью PublicKey, несущего тот
же секрет и алгоритм.
<?php
require 'vendor/autoload.php';
use Flytachi\Jwt\JWT;
use Flytachi\Jwt\Entity\PublicKey;
use Flytachi\Jwt\JWTException;
$secret = getenv('JWT_SECRET');
$token = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
$token = str_replace('Bearer ', '', $token);
try {
$payload = JWT::decode($token, [new PublicKey($secret, 'HS256')]);
$userId = $payload->getClaim('sub');
echo "Authenticated as {$userId}\n";
} catch (JWTException $e) {
http_response_code(401);
echo "Invalid token: {$e->getMessage()}\n";
}Проверка делает три вещи сразу
decode() проверяет подпись (обнаружение подмены), подтверждает, что алгоритм ключа
совпадает с заголовком токена, и проверяет exp/nbf/iat. Любой сбой выбрасывает
одно JWTException, поэтому один catch покрывает их все.
Шаг 3 — Убедитесь, что он отклоняет плохой токен
Докажите, что гарантии соблюдаются. Подмените токен или дождитесь его истечения:
// Токен, чей секрет не совпадает → проверка подписи не проходит
try {
JWT::decode($token, [new PublicKey('wrong-secret', 'HS256')]);
} catch (JWTException $e) {
echo $e->getMessage(); // Signature verification failed.
}
// Уже истёкший токен → проверка временного claim не проходит
$expired = JWT::encode(
new JwtPayload(['sub' => 'user-42', 'exp' => time() - 10]),
new PrivateKey($secret, 'HS256')
);
try {
JWT::decode($expired, [new PublicKey($secret, 'HS256')]);
} catch (JWTException $e) {
echo $e->getMessage(); // Token has expired (exp).
}Всё целиком
Полный, готовый к копированию цикл:
<?php
require 'vendor/autoload.php';
use Flytachi\Jwt\JWT;
use Flytachi\Jwt\Entity\JwtPayload;
use Flytachi\Jwt\Entity\PrivateKey;
use Flytachi\Jwt\Entity\PublicKey;
use Flytachi\Jwt\JWTException;
$secret = 'change-me-to-a-strong-secret';
$now = time();
// Выпуск
$token = JWT::encode(
new JwtPayload([
'iss' => 'https://my-app.com',
'sub' => 'user-42',
'iat' => $now,
'exp' => $now + 3600,
]),
new PrivateKey($secret, 'HS256')
);
// Проверка
try {
$payload = JWT::decode($token, [new PublicKey($secret, 'HS256')]);
echo "OK, user = " . $payload->getClaim('sub') . "\n";
} catch (JWTException $e) {
echo "Rejected: " . $e->getMessage() . "\n";
}Запустите его:
php jwt-demo.php
# OK, user = user-42Что только что произошло
encode()построил заголовок, закодировал заголовок и payload в base64url, подписал пару и объединил их в строкуheader.payload.signature, которую вы видели.decode()обратил это: разбил токен, проверил подпись с помощьюhash_equals, убедился, что алгоритм совпадает, и проверил временные claims.- Один секрет и подписал, и проверил, потому что
HS256симметричен. С RSA/ECDSA это становится двумя разными ключами.
Следующие шаги
- «Модель работы» — что на самом деле внутри JWT
- «HMAC-токены» — больше рецептов с общим секретом
- «Асимметричные ключи» — подпись/проверка RSA и ECDSA
- «Работа с claims» — истечение, допуск, собственные claims
- «Справочник API» — каждый класс и метод