15%

Сэкономьте 15% на всех хостинговых услугах

Проверьте свои навыки и получите скидку на любой тарифный план

Используйте код:

Skills
Начать
08.10.2024

Создание безопасного 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_*`
  • Базовое знакомство с сервисным контейнером Laravel, middleware и Eloquent ORM

Если вы развёртываете этот 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 зависят от него, и его удаление вызывает неожиданные сбои в консольных командах и обработчиках очередей, взаимодействующих с системой аутентификации.

Шаг 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.

Как работает чёрный список:

  1. При выходе из системы утверждение `jti` (JWT ID) токена сохраняется в кэше с TTL, соответствующим оставшемуся времени жизни токена.
  2. При каждом аутентифицированном запросе middleware проверяет чёрный список перед принятием токена.
  3. Параметр `blacklist_grace_period` допускает короткое окно, в течение которого обновляемый токен всё ещё может использоваться, предотвращая состояния гонки в клиентах, выполняющих параллельные запросы.

Убедитесь, что ваш драйвер кэша поддерживает это. Стандартный драйвер `file` работает для развёртываний на одном сервере. Для горизонтально масштабируемых API, работающих на нескольких узлах — что характерно при использовании Выделенных серверов в конфигурации с балансировкой нагрузки — переключитесь на 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 или аналогичный механизм во фронтенде для прозрачной обработки обновления токенов.

Шаг 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 против сессий, Laravel Sanctum и Passport

Понимание того, когда JWT является правильным выбором, требует сравнения с альтернативами, доступными в экосистеме Laravel.

КритерийJWT (`tymon/jwt-auth`)Laravel SanctumLaravel PassportНа основе сессий
**Сохранение состояния**Без состоянияБез состояния (токены SPA) / С состоянием (куки)С состоянием (токены в БД)С состоянием
**Хранение токенов**На стороне клиентаНа стороне клиента или в кукиБаза данныхСерверная сессия
**Отзыв**Чёрный список (кэш)Немедленный (удаление из БД)Немедленный (удаление из БД)Немедленный
**Масштабируемость**Отличная (нет обращений к БД на запрос)ХорошаяУмеренная (обращение к БД на каждый запрос)Низкая (требуется синхронизация сессий)
**Поддержка OAuth2**НетНетДа (полный OAuth2-сервер)Нет
**Сложность**СредняяНизкаяВысокаяНизкая
**Лучше всего для**Stateless API, микросервисыSPA, мобильные приложенияСторонние OAuth-клиентыТрадиционные веб-приложения
**Интроспекция токена**Декодирование полезной нагрузки на стороне клиентаТребует вызова APIТребует вызова APIН/П

Когда выбирать 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. Применяйте ограничение частоты запросов к конечным точкам аутентификации

Встроенный middleware throttle 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 вы можете управлять правами доступа и владельцем файлов через Файловый менеджер или 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:

  • Согласованность часовых поясов: Утверждения JWT `iat`, `nbf` и `exp` являются Unix-временными метками. Убедитесь, что часовой пояс вашего сервера установлен на UTC (`date.timezone = UTC` в `php.ini`), чтобы предотвратить отклонение токенов из-за расхождения часов.
  • OPcache: Включите PHP OPcache для снижения накладных расходов на загрузку файлов библиотеки JWT при каждом запросе. Это особенно важно для высоконагруженных API.
  • Обработчики очередей для очистки чёрного списка токенов: Если вы реализуете пользовательские задания по очистке чёрного списка токенов, убедитесь, что обработчик очереди запущен как управляемый процесс (Supervisor или systemd).
  • Управление переменными среды: На Панелях управления VPS используйте менеджер переменных среды панели, а не редактируйте `.env` напрямую в продакшене, чтобы избежать случайной перезаписи при развёртываниях.

Контрольный список перед запуском в продакшен

Используйте этот контрольный список для проверки готовности реализации к продакшену:

  • `JWT_SECRET` содержит не менее 32 символов, сгенерирован случайным образом и не зафиксирован в системе контроля версий
  • `blacklist_enabled` установлен в `true` в `config/jwt.php`
  • TTL токена составляет 30 минут или менее для токенов доступа
  • TTL обновления установлен в соответствии с вашей политикой сессий
  • Все конечные точки API обслуживаются исключительно по HTTPS
  • Ограничение частоты запросов применено к конечным точкам `/login` и `/register`
  • Обработчик исключений `unauthenticated` возвращает JSON, а не HTML-перенаправление
  • Пользовательские утверждения не содержат паролей, секретов или конфиденциальных персональных данных
  • Драйвер кэша — Redis или Memcached (не `file`) в многосерверных развёртываниях
  • События аутентификации регистрируются и отслеживаются
  • Изменения ролей, требующие немедленного вступления в силу, обрабатываются через добавление в чёрный список, а не только через истечение срока действия утверждений
  • Права доступа к файлу `.env` ограничены пользователем веб-сервера

Часто задаваемые вопросы

В чём разница между `JWT_SECRET` и `APP_KEY` в Laravel?

`APP_KEY` используется сервисом шифрования Laravel для шифрования куки, данных сессий и значений, передаваемых через `Crypt::encrypt()`. `JWT_SECRET` используется исключительно пакетом `tymon/jwt-auth` для подписи и проверки JSON Web Token. Они криптографически независимы и служат совершенно разным целям. Оба должны храниться в тайне.

Почему мой JWT-токен продолжает возвращать 401, хотя срок его действия не истёк?

Наиболее распространённые причины: токен добавлен в чёрный список (например, после выхода из системы или обновления), `JWT_SECRET` был ротирован после выдачи токена, драйвер кэша, хранящий чёрный список, недоступен, или существует расхождение часов между сервером выдачи и сервером проверки, превышающее параметр `leeway` в `config/jwt.php`. Проверяйте каждую из этих причин по порядку.

Можно ли использовать JWT-аутентификацию с очередями Laravel или консольными командами?

JWT предназначен для аутентификации HTTP-запросов. Внутри заданий очереди или команд Artisan нет контекста HTTP-запроса, поэтому вы не можете разрешить пользователя из токена через middleware. Вместо этого передайте первичный ключ пользователя в качестве параметра задания и загружайте модель напрямую с помощью `User::find($userId)`.

Как обрабатывать параллельные запросы во время обновления токена без получения ошибок 401?

Установите `blacklist_grace_period` в `config/jwt.php` в значение от 10 до 30 секунд. В течение этого окна токен, который только что был обновлён (и технически добавлен в чёрный список), всё ещё будет приниматься. Это предотвращает состояния гонки в клиентах, отправляющих несколько одновременных запросов во время выполнения обновления.

Совместим ли `tymon/jwt-auth` с Laravel 11?

По состоянию на текущий цикл выпусков, версия `tymon/jwt-auth` `2.x` поддерживает Laravel 10 и 11 через ветку `dev-develop` или теговые релизы, декларирующие совместимость. Всегда проверяйте ограничения `composer.json` пакета и трекер задач на GitHub перед обновлением версий Laravel в проекте, зависящем от этого пакета. Рассмотрите возможность фиксации версии пакета в `composer.json`, чтобы избежать неожиданных критических изменений при выполнении `composer update`.

15%

Сэкономьте 15% на всех хостинговых услугах

Проверьте свои навыки и получите скидку на любой тарифный план

Используйте код:

Skills
Начать