Изграждане на сигурен Laravel API с JWT удостоверяване
JWT (JSON Web Token) удостоверяването в Laravel предоставя безсесиен, криптографски подписан механизъм за верифициране на API потребители без съхранение на сесии от страна на сървъра. JWT кодира полезен товар — обикновено потребителска идентичност и претенции — в компактен, URL-безопасен низ, подписан с таен или RSA ключ, което позволява на всяка услуга, притежаваща ключа за верификация, да валидира токена независимо.
Това ръководство обхваща пълното внедряване на JWT удостоверяване в Laravel API с помощта на пакета `tymon/jwt-auth`, включително настройка, конфигурация на модела, логика на контролера, защита на маршрути, стратегия за обновяване на токени и укрепване за производствена среда. Всяка стъпка включва технически контекст, надхвърлящ повърхностните уроци.
Предварителни изисквания и предположения за средата
Преди да започнете, потвърдете следното:
- PHP 8.1 или по-висока версия (PHP 8.2 се препоръчва за Laravel 11)
- Laravel 10 или 11, инсталиран чрез Composer
- Composer 2.x
- MySQL 8.0, PostgreSQL 15 или друга PDO-съвместима база данни
- Конфигуриран файл `.env` с валидни идентификационни данни `DB_*`
- Основно познаване на service container, middleware и Eloquent ORM на Laravel
Ако разгръщате това API в производствена среда, план за VPS Хостинг с пълен root достъп ви дава необходимия контрол за конфигуриране на PHP-FPM, сигурно управление на променливи на средата и правилно задаване на файлови разрешения — всичко критично за управлението на JWT тайни.
Стъпка 1: Създаване на нов Laravel проект
“`bash
composer create-project laravel/laravel laravel-jwt-api
cd laravel-jwt-api
“`
Проверете инсталацията:
“`bash
php artisan –version
“`
Задайте ключа на приложението, ако не е генериран автоматично:
“`bash
php artisan key:generate
“`
`APP_KEY` в `.env` е отделен от JWT тайната. И двата са необходими и служат за различни криптографски цели — `APP_KEY` защитава криптирани бисквитки и данни от сесии, докато `JWT_SECRET` подписва токени.
Стъпка 2: Инсталиране на пакета за JWT удостоверяване
Пакетът `tymon/jwt-auth` е де факто стандарт за JWT в Laravel. Инсталирайте го:
“`bash
composer require tymon/jwt-auth
“`
Публикувайте конфигурацията на пакета:
“`bash
php artisan vendor:publish –provider="TymonJWTAuthProvidersLaravelServiceProvider"
“`
Това създава `config/jwt.php`, който контролира TTL на токена, TTL на обновяването, алгоритъма, поведението на черния списък и необходимите претенции. Прегледайте внимателно този файл — стойностите по подразбиране са разумни за разработка, но изискват настройка за производствена среда.
Ключови конфигурационни параметри в `config/jwt.php`:
| Параметър | По подразбиране | Препоръка за производствена среда |
|---|---|---|
| — | — | — |
| `ttl` | 60 минути | 15–30 минути |
| `refresh_ttl` | 20160 минути (2 седмици) | 1440–10080 минути |
| `algo` | `HS256` | `RS256` за разпределени системи |
| `blacklist_enabled` | `true` | Трябва да бъде `true` |
| `blacklist_grace_period` | 0 секунди | 10–30 секунди за едновременни заявки |
| `required_claims` | `['iss','iat','exp','nbf','sub','jti']` | Запазете всички; добавете `aud` за многонаемателни API |
Стъпка 3: Генериране на JWT таен ключ
“`bash
php artisan jwt:secret
“`
Това добавя `JWT_SECRET` към вашия файл `.env`. Тази стойност се използва като HMAC-SHA256 ключ за подписване на всички токени при използване на алгоритъма `HS256` по подразбиране.
Критични бележки за сигурността:
- Никога не предавайте `.env` към система за контрол на версиите. Добавете го незабавно към `.gitignore`.
- При споделен тръбопровод за разгръщане, инжектирайте `JWT_SECRET` като променлива на средата чрез вашата CI/CD система, вместо да го съхранявате във файл.
- Ако ротирате тайната, всички съществуващи токени се анулират незабавно. Планирайте прозорците за ротация съответно и ги съобщете на потребителите на API.
- За микросервизни архитектури, където множество услуги трябва да верифицират токени, преминете към `RS256`. Генерирайте RSA двойка ключове, съхранете частния ключ в услугата за удостоверяване и разпространете само публичния ключ до потребляващите услуги.
Стъпка 4: Конфигуриране на охраната за удостоверяване
Отворете `config/auth.php` и актуализирайте секциите за defaults и guards:
“`php
'defaults' => [
'guard' => 'api',
'passwords' => 'users',
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],
“`
Това инструктира системата за удостоверяване на Laravel да използва JWT драйвера при разрешаване на охраната `api`. Middleware-ът `auth:api` вече ще делегира валидирането на токени към `tymon/jwt-auth` вместо към Laravel Passport или драйвера за токени по подразбиране.
Не премахвайте охраната `web`. Много вътрешни компоненти на Laravel зависят от нея и премахването й причинява неочаквани грешки в конзолни команди и queue workers, които взаимодействат със системата за удостоверяване.
Стъпка 5: Създаване на потребителски модел и миграция
Ако моделът `User` и миграцията по подразбиране вече съществуват (те съществуват при нова инсталация на Laravel), можете да ги модифицирате директно. Ако започвате от нулата:
“`bash
php artisan make:model User -m
“`
Отворете файла за миграция в `database/migrations/` и дефинирайте схемата:
“`php
public function up(): void
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}
“`
Изпълнете миграцията:
“`bash
php artisan migrate
“`
Бележка за производствена среда: Винаги изпълнявайте миграции с флага `–force` в производствени среди, където `APP_ENV=production`, тъй като Laravel ще поиска потвърждение в противен случай:
“`bash
php artisan migrate –force
“`
Стъпка 6: Имплементиране на интерфейса JWTSubject в потребителския модел
Пакетът `tymon/jwt-auth` изисква вашият модел за удостоверяване да имплементира договора `JWTSubject`. Отворете `app/Models/User.php`:
“`php
<?php
namespace AppModels;
use IlluminateDatabaseEloquentFactoriesHasFactory;
use IlluminateFoundationAuthUser as Authenticatable;
use IlluminateNotificationsNotifiable;
use TymonJWTAuthContractsJWTSubject;
class User extends Authenticatable implements JWTSubject
{
use HasFactory, Notifiable;
protected $fillable = [
'name',
'email',
'password',
];
protected $hidden = [
'password',
'remember_token',
];
protected $casts = [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
/**
- Get the identifier that will be stored in the JWT subject claim.
*/
public function getJWTIdentifier(): mixed
{
return $this->getKey();
}
/**
- Return a key-value array of arbitrary claims to add to the JWT payload.
*/
public function getJWTCustomClaims(): array
{
return [];
}
}
“`
Относно персонализираните претенции: Методът `getJWTCustomClaims()` е мястото, където вграждате специфични за приложението данни директно в полезния товар на токена. Честите случаи на употреба включват вграждане на `role`, `tenant_id` или `permissions`, така че последващите услуги да могат да вземат решения за оторизация без заявка към базата данни. Бъдете внимателни какво вграждате — всяка претенция увеличава размера на токена и е четима от всеки, който base64-декодира полезния товар. Никога не вграждайте чувствителни данни като пароли или лична информация.
Стъпка 7: Изграждане на контролера за удостоверяване
“`bash
php artisan make:controller AuthController
“`
Попълнете `app/Http/Controllers/AuthController.php` с пълна логика за удостоверяване:
“`php
<?php
namespace AppHttpControllers;
use AppModelsUser;
use IlluminateHttpJsonResponse;
use IlluminateHttpRequest;
use IlluminateSupportFacadesAuth;
use IlluminateSupportFacadesHash;
use IlluminateValidationValidationException;
use TymonJWTAuthExceptionsJWTException;
use TymonJWTAuthFacadesJWTAuth;
class AuthController extends Controller
{
/**
- Register a new user and return a JWT.
*/
public function register(Request $request): JsonResponse
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:8|confirmed',
]);
$user = User::create([
'name' => $validated['name'],
'email' => $validated['email'],
'password' => Hash::make($validated['password']),
]);
$token = JWTAuth::fromUser($user);
return response()->json([
'token' => $token,
'token_type' => 'bearer',
'expires_in' => auth('api')->factory()->getTTL() * 60,
], 201);
}
/**
- Authenticate a user and return a JWT.
*/
public function login(Request $request): JsonResponse
{
$credentials = $request->validate([
'email' => 'required|string|email',
'password' => 'required|string',
]);
if (!$token = Auth::guard('api')->attempt($credentials)) {
return response()->json(['error' => 'Invalid credentials'], 401);
}
return $this->respondWithToken($token);
}
/**
- Invalidate the current token (logout).
*/
public function logout(): JsonResponse
{
try {
Auth::guard('api')->logout();
} catch (JWTException $e) {
return response()->json(['error' => 'Failed to invalidate token'], 500);
}
return response()->json(['message' => 'Successfully logged out']);
}
/**
- Refresh the current token.
*/
public function refresh(): JsonResponse
{
try {
$token = Auth::guard('api')->refresh();
} catch (JWTException $e) {
return response()->json(['error' => 'Token cannot be refreshed'], 401);
}
return $this->respondWithToken($token);
}
/**
- Return the authenticated user's profile.
*/
public function me(): JsonResponse
{
return response()->json(Auth::guard('api')->user());
}
/**
- Format the token response consistently.
*/
protected function respondWithToken(string $token): JsonResponse
{
return response()->json([
'token' => $token,
'token_type' => 'bearer',
'expires_in' => auth('api')->factory()->getTTL() * 60,
]);
}
}
“`
Защо изричното указване на охраната е важно: Извикването на `Auth::attempt()` без указване на охраната се връща към охраната по подразбиране. Ако сте променили стойността по подразбиране на `api`, това работи — но е нестабилно. Винаги извиквайте `Auth::guard('api')->attempt()` изрично, за да избегнете фини грешки, когато охраната по подразбиране се промени по време на рефакториране.
Стъпка 8: Дефиниране на API маршрути
Отворете `routes/api.php` и дефинирайте маршрутите за удостоверяване и защитените маршрути:
“`php
<?php
use AppHttpControllersAuthController;
use IlluminateSupportFacadesRoute;
// Public authentication routes
Route::prefix('auth')->group(function () {
Route::post('register', [AuthController::class, 'register']);
Route::post('login', [AuthController::class, 'login']);
});
// Protected routes — require a valid JWT
Route::middleware('auth:api')->prefix('auth')->group(function () {
Route::post('logout', [AuthController::class, 'logout']);
Route::post('refresh', [AuthController::class, 'refresh']);
Route::get('me', [AuthController::class, 'me']);
});
// Example protected resource routes
Route::middleware('auth:api')->group(function () {
Route::apiResource('posts', AppHttpControllersPostController::class);
});
“`
Стратегия за префикс на маршрути: Групирането на крайните точки за удостоверяване под `/api/auth/` е широко възприета конвенция, която прави документацията на API по-чиста и опростява правилата за ограничаване на скоростта — можете да приложите по-строго ограничаване към `/api/auth/login` независимо от крайните точки за ресурси.
Стъпка 9: Защита на маршрути и обработка на грешки в middleware
Middleware-ът `auth:api` на Laravel ще върне отговор `401 Unauthenticated`, когато токенът липсва, е изтекъл или е невалиден. За да върнете последователен JSON отговор вместо HTML пренасочване, заменете метода `unauthenticated` в `app/Exceptions/Handler.php`:
“`php
use IlluminateAuthAuthenticationException;
use IlluminateHttpRequest;
protected function unauthenticated($request, AuthenticationException $exception)
{
if ($request->expectsJson() || $request->is('api/*')) {
return response()->json(['error' => 'Unauthenticated'], 401);
}
return redirect()->guest(route('login'));
}
“`
Без това заместване, API клиентите получават HTML 302 пренасочване към страница за вход, която не съществува в чисто API приложение — честа причина за объркване по време на интеграционно тестване.
Стъпка 10: Стратегия за обновяване на токени и черен списък
Безсесийната природа на JWT създава напрежение: токените са самостоятелни и валидни до изтичане, но се нуждаете от начин да ги анулирате при излизане. Пакетът `tymon/jwt-auth` разрешава това с черен списък на токени, поддържан от Laravel cache драйвера.
Как работи черният списък:
- При излизане, претенцията `jti` (JWT ID) на токена се съхранява в кеша с TTL, съответстващ на оставащото времетраене на токена.
- При всяка удостоверена заявка, middleware-ът проверява черния списък преди да приеме токена.
- Настройката `blacklist_grace_period` позволява кратък прозорец, в който токен, който се обновява, все още може да се използва, предотвратявайки race conditions в клиенти, правещи едновременни заявки.
Уверете се, че вашият cache драйвер поддържа това. Драйверът `file` по подразбиране работи за разгръщания на единичен сървър. За хоризонтално мащабирани API, работещи на множество възли — обичайно при използване на Dedicated Servers в конфигурация с балансиране на натоварването — преминете към Redis или Memcached:
“`env
CACHE_DRIVER=redis
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
“`
Поток за обновяване на токени:
“`
Client API Server
| — POST /api/auth/refresh ——> | |
| Authorization: Bearer <old> | |
| — Validate old token | |
| — Blacklist old token's jti | |
| — Issue new token | |
| <– 200 { token: <new> } ——– |
“`
Старият токен се добавя незабавно в черния списък при обновяване. Клиентите трябва да съхранят новия токен и да изхвърлят стария. Имплементирайте това като Axios interceptor или еквивалент във вашия frontend за прозрачно обновяване на токени.
Стъпка 11: Тестване на API
Използвайте `curl` или Postman за верифициране на всяка крайна точка.
Регистриране на потребител:
“`bash
curl -X POST https://your-domain.com/api/auth/register
-H "Content-Type: application/json"
-d '{
"name": "Jane Smith",
"email": "jane@example.com",
"password": "SecurePass123!",
"password_confirmation": "SecurePass123!"
}'
“`
Очакван отговор (`201 Created`):
“`json
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9…",
"token_type": "bearer",
"expires_in": 3600
}
“`
Влизане:
“`bash
curl -X POST https://your-domain.com/api/auth/login
-H "Content-Type: application/json"
-d '{"email": "jane@example.com", "password": "SecurePass123!"}'
“`
Достъп до защитен маршрут:
“`bash
curl -X GET https://your-domain.com/api/auth/me
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9…"
“`
Обновяване на токена:
“`bash
curl -X POST https://your-domain.com/api/auth/refresh
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9…"
“`
Излизане:
“`bash
curl -X POST https://your-domain.com/api/auth/logout
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9…"
“`
JWT срещу Session срещу Laravel Sanctum срещу Passport
Разбирането кога JWT е правилният избор изисква сравнение с алтернативите, налични в екосистемата на Laravel.
| Критерий | JWT (`tymon/jwt-auth`) | Laravel Sanctum | Laravel Passport | Базирано на сесии |
|---|---|---|---|---|
| — | — | — | — | — |
| **Сесийност** | Безсесийно | Безсесийно (SPA токени) / Сесийно (бисквитки) | Сесийно (DB токени) | Сесийно |
| **Съхранение на токени** | От страна на клиента | От страна на клиента или бисквитка | База данни | Сесия от страна на сървъра |
| **Анулиране** | Черен списък (кеш) | Незабавно (изтриване от DB) | Незабавно (изтриване от DB) | Незабавно |
| **Мащабируемост** | Отлична (без DB заявка на request) | Добра | Умерена (DB заявка на request) | Слаба (необходима синхронизация на сесии) |
| **Поддръжка на OAuth2** | Не | Не | Да (пълен OAuth2 сървър) | Не |
| **Сложност** | Средна | Ниска | Висока | Ниска |
| **Най-подходящо за** | Безсесийни API, микросервизи | SPA, мобилни приложения | OAuth клиенти на трети страни | Традиционни уеб приложения |
| **Интроспекция на токени** | Декодиране на полезния товар от страна на клиента | Изисква API заявка | Изисква API заявка | N/A |
Кога да изберете JWT пред Sanctum: Ако вашият API се използва от клиенти на трети страни, мобилни приложения или микросервизи, които не могат да споделят сесийна бисквитка или връзка с база данни с вашето Laravel приложение, самостоятелната природа на JWT е значително предимство. Ако изграждате SPA от първа страна на същия домейн, Sanctum с удостоверяване базирано на бисквитки е по-просто и напълно избягва проблемите със сигурността при съхранение на токени.
Укрепване на сигурността за производствена среда
Работещото JWT внедряване не е сигурно по подразбиране. Приложете тези мерки за укрепване преди пускане в производствена среда:
1. Прилагайте HTTPS безусловно
JWT токените, предавани по HTTP, могат тривиално да бъдат прихванати. Прилагайте TLS на ниво уеб сървър и пренасочвайте целия HTTP трафик. Комбинирайте това с SSL Сертификат, за да осигурите криптиран транспорт за всяка API заявка.
2. Задайте агресивни TTL на токените
Краткотрайните токени за достъп (15–30 минути) в комбинация с по-дълготрайни токени за обновяване (7–14 дни) ограничават щетите от откраднат токен. Актуализирайте `config/jwt.php`:
“`php
'ttl' => 15,
'refresh_ttl' => 10080,
“`
3. Прилагайте ограничаване на скоростта към крайните точки за удостоверяване
Вграденият throttle middleware на Laravel предотвратява атаки с груба сила:
“`php
Route::middleware(['throttle:10,1'])->group(function () {
Route::post('auth/login', [AuthController::class, 'login']);
Route::post('auth/register', [AuthController::class, 'register']);
});
“`
Това ограничава всеки IP до 10 заявки в минута към крайните точки за удостоверяване.
4. Валидирайте претенцията `aud` за многонаемателни API
Ако вашият API обслужва множество клиентски приложения, вградете и валидирайте претенция `audience`, за да предотвратите повторно използване на токени между услуги:
“`php
// In getJWTCustomClaims()
return [
'aud' => config('app.jwt_audience'),
];
“`
5. Защитете JWT_SECRET на ниво ОС
Задайте ограничителни файлови разрешения за `.env`:
“`bash
chmod 640 .env
chown www-data:www-data .env
“`
На правилно конфигуриран VPS с cPanel, можете да управлявате собствеността и разрешенията на файлове чрез File Manager или SSH без риск от излагане на тайни на други потребители в системата.
6. Регистрирайте събития за удостоверяване
Интегрирайте системата за събития на Laravel за регистриране на неуспешни опити за влизане, обновявания на токени и излизания в централизирана услуга за регистриране. Това е от съществено значение за откриване на аномалии.
Добавяне на контрол на достъпа базиран на роли
Персонализираните претенции правят лесно вграждането на роли директно в токена:
“`php
// In User model
public function getJWTCustomClaims(): array
{
return [
'role' => $this->role, // e.g., 'admin', 'editor', 'viewer'
];
}
“`
Създайте middleware за прилагане на изисквания за роли:
“`php
<?php
namespace AppHttpMiddleware;
use Closure;
use IlluminateHttpRequest;
use TymonJWTAuthFacadesJWTAuth;
class RoleMiddleware
{
public function handle(Request $request, Closure $next, string $role): mixed
{
$payload = JWTAuth::parseToken()->getPayload();
if ($payload->get('role') !== $role) {
return response()->json(['error' => 'Forbidden'], 403);
}
return $next($request);
}
}
“`
Регистрирайте го в `app/Http/Kernel.php` (Laravel 10) или `bootstrap/app.php` (Laravel 11) и го приложете към маршрути:
“`php
Route::middleware(['auth:api', 'role:admin'])->group(function () {
Route::apiResource('admin/users', AdminUserController::class);
});
“`
Предупреждение: Данните за роли, вградени в токена, са актуални само до момента на издаване на токена. Ако ролята на потребителя се промени, старият токен продължава да предоставя старата роля до изтичането му или обновяването му. За промени в ролите с висока сигурност, изискващи незабавен ефект (напр. незабавно отнемане на администраторски достъп), комбинирайте черния списък на токени с кратък TTL или извършете проверка на ролята в базата данни в middleware заедно с проверката на претенцията.
Съображения за разгръщане на Laravel JWT API
При преминаване от локална разработка към производствен сървър, няколко специфични за средата фактора влияят на поведението на JWT:
- Последователност на часовата зона: Претенциите `iat`, `nbf` и `exp` на JWT са Unix времеви марки. Уверете се, че часовата зона на вашия сървър е зададена на UTC (`date.timezone = UTC` в `php.ini`), за да предотвратите отхвърляне на токени поради разлика в часовника.
- OPcache: Активирайте PHP OPcache, за да намалите натоварването от зареждане на файловете на JWT библиотеката при всяка заявка. Това е особено значимо при API с висок трафик.
- Queue workers за почистване на токени: Ако имплементирате персонализирани задачи за почистване на черния списък на токени, уверете се, че вашият queue worker работи като наблюдаван процес (Supervisor или systemd).
- Управление на променливи на средата: На VPS контролни панели, използвайте мениджъра на променливи на средата на панела, вместо да редактирате `.env` директно в производствена среда, за да избегнете случайно презаписване по време на разгръщания.
Контролен списък за решения преди пускане в производствена среда
Използвайте този контролен списък, за да проверите дали вашето внедряване е готово за производствена среда:
- `JWT_SECRET` е поне 32 символа, генериран произволно и не е предаден към система за контрол на версиите
- `blacklist_enabled` е зададен на `true` в `config/jwt.php`
- TTL на токена е 30 минути или по-малко за токени за достъп
- TTL на обновяването е зададен на стойност, подходяща за вашата политика за сесии
- Всички API крайни точки се обслужват изключително по HTTPS
- Ограничаването на скоростта е приложено към крайните точки `/login` и `/register`
- Обработчикът на изключения `unauthenticated` връща JSON, а не HTML пренасочване
- Персонализираните претенции не съдържат пароли, тайни или чувствителна лична информация
- Cache драйверът е Redis или Memcached (не `file`) при разгръщания на множество сървъри
- Събитията за удостоверяване се регистрират и наблюдават
- Промените в ролите, изискващи незабавен ефект, се обработват чрез черен списък, а не само чрез изтичане на претенции
- Файловите разрешения за `.env` са ограничени до потребителя на уеб сървъра
ЧЗВ
Каква е разликата между `JWT_SECRET` и `APP_KEY` в Laravel?
`APP_KEY` се използва от услугата за криптиране на Laravel за криптиране на бисквитки, данни от сесии и стойности, предавани чрез `Crypt::encrypt()`. `JWT_SECRET` се използва изключително от `tymon/jwt-auth` за подписване и верифициране на JSON Web Tokens. Те са криптографски независими и служат за напълно различни цели. И двата трябва да се пазят в тайна.
Защо моят JWT токен продължава да връща 401, въпреки че не е изтекъл?
Най-честите причини са: токенът е добавен в черния списък (напр. след излизане или обновяване), `JWT_SECRET` е ротиран след издаването на токена, cache драйверът, съхраняващ черния списък, е недостъпен, или има разлика в часовника между издаващия сървър и валидиращия сървър, надвишаваща настройката `leeway` в `config/jwt.php`. Проверете всяко от тях по ред.
Мога ли да използвам JWT удостоверяване с Laravel queues или конзолни команди?
JWT е проектиран за удостоверяване на HTTP заявки. Вътре в queue jobs или Artisan команди няма контекст на HTTP заявка, така че не можете да разрешите потребител от токен чрез middleware. Вместо това, предайте първичния ключ на потребителя като параметър на задачата и заредете модела директно с `User::find($userId)`.
Как да обработвам едновременни заявки по време на обновяване на токени без получаване на грешки 401?
Задайте `blacklist_grace_period` в `config/jwt.php` на стойност между 10 и 30 секунди. По време на този прозорец, токен, който току-що е бил обновен (и технически добавен в черния списък), все още ще бъде приет. Това предотвратява race conditions в клиенти, изпращащи множество едновременни заявки, докато обновяването е в ход.
Съвместим ли е `tymon/jwt-auth` с Laravel 11?
Към текущия цикъл на издания, `tymon/jwt-auth` версия `2.x` поддържа Laravel 10 и 11 чрез клона `dev-develop` или маркирани издания, декларирали съвместимост. Винаги проверявайте ограниченията `composer.json` на пакета и проследявача на проблеми в GitHub преди надграждане на версиите на Laravel в проект, зависещ от този пакет. Обмислете фиксиране на версията на пакета в `composer.json`, за да избегнете неочаквани промени, нарушаващи съвместимостта по време на `composer update`.
