Budowanie bezpiecznego API Laravel z uwierzytelnianiem JWT
Uwierzytelnianie JWT (JSON Web Token) w Laravel zapewnia bezstanowy, kryptograficznie podpisany mechanizm weryfikacji konsumentów API bez przechowywania sesji po stronie serwera. JWT koduje ładunek — zazwyczaj tożsamość użytkownika i roszczenia — w kompaktowy, bezpieczny dla URL ciąg znaków podpisany kluczem tajnym lub RSA, umożliwiając każdej usłudze posiadającej klucz weryfikacyjny niezależną walidację tokenu.
Ten przewodnik obejmuje pełną implementację uwierzytelniania JWT w API Laravel przy użyciu pakietu `tymon/jwt-auth`, w tym konfigurację, konfigurację modelu, logikę kontrolera, ochronę tras, strategię odświeżania tokenów i utwardzanie produkcyjne. Każdy krok zawiera kontekst techniczny wykraczający poza powierzchowne samouczki.
Wymagania wstępne i założenia środowiskowe
Przed rozpoczęciem potwierdź następujące kwestie:
- PHP 8.1 lub wyższy (PHP 8.2 zalecany dla Laravel 11)
- Laravel 10 lub 11 zainstalowany przez Composer
- Composer 2.x
- MySQL 8.0, PostgreSQL 15 lub dowolna baza danych kompatybilna z PDO
- Skonfigurowany plik `.env` z prawidłowymi poświadczeniami `DB_*`
- Podstawowa znajomość kontenera usług Laravel, middleware i Eloquent ORM
Jeśli wdrażasz to API w środowisku produkcyjnym, plan Hostingu VPS z pełnym dostępem root daje Ci kontrolę potrzebną do konfiguracji PHP-FPM, bezpiecznego zarządzania zmiennymi środowiskowymi i prawidłowego ustawiania uprawnień do plików — wszystko to jest kluczowe dla zarządzania kluczem tajnym JWT.
Krok 1: Utwórz nowy projekt Laravel
“`bash
composer create-project laravel/laravel laravel-jwt-api
cd laravel-jwt-api
“`
Zweryfikuj instalację:
“`bash
php artisan –version
“`
Ustaw klucz aplikacji, jeśli nie został wygenerowany automatycznie:
“`bash
php artisan key:generate
“`
`APP_KEY` w `.env` jest oddzielny od klucza tajnego JWT. Oba są wymagane i służą różnym celom kryptograficznym — `APP_KEY` chroni zaszyfrowane pliki cookie i dane sesji, podczas gdy `JWT_SECRET` podpisuje tokeny.
Krok 2: Zainstaluj pakiet uwierzytelniania JWT
Pakiet `tymon/jwt-auth` jest de facto standardem dla JWT w Laravel. Zainstaluj go:
“`bash
composer require tymon/jwt-auth
“`
Opublikuj konfigurację pakietu:
“`bash
php artisan vendor:publish –provider="TymonJWTAuthProvidersLaravelServiceProvider"
“`
Tworzy to `config/jwt.php`, który kontroluje TTL tokenu, TTL odświeżania, algorytm, zachowanie czarnej listy i wymagane roszczenia. Przejrzyj ten plik uważnie — wartości domyślne są rozsądne dla środowiska deweloperskiego, ale wymagają dostrojenia dla produkcji.
Kluczowe parametry konfiguracji w `config/jwt.php`:
| Parametr | Domyślny | Zalecenie produkcyjne |
|---|---|---|
| — | — | — |
| `ttl` | 60 minut | 15–30 minut |
| `refresh_ttl` | 20160 minut (2 tygodnie) | 1440–10080 minut |
| `algo` | `HS256` | `RS256` dla systemów rozproszonych |
| `blacklist_enabled` | `true` | Musi być `true` |
| `blacklist_grace_period` | 0 sekund | 10–30 sekund dla równoczesnych żądań |
| `required_claims` | `['iss','iat','exp','nbf','sub','jti']` | Zachowaj wszystkie; dodaj `aud` dla API wielodostępnych |
Krok 3: Wygeneruj klucz tajny JWT
“`bash
php artisan jwt:secret
“`
Dołącza to `JWT_SECRET` do pliku `.env`. Ta wartość jest używana jako klucz podpisywania HMAC-SHA256 dla wszystkich tokenów przy użyciu domyślnego algorytmu `HS256`.
Krytyczne uwagi dotyczące bezpieczeństwa:
- Nigdy nie commituj `.env` do kontroli wersji. Natychmiast dodaj go do `.gitignore`.
- W przypadku współdzielonego potoku wdrożeniowego, wstrzykuj `JWT_SECRET` jako zmienną środowiskową przez system CI/CD zamiast przechowywać go w pliku.
- Jeśli rotacja klucza tajnego zostanie przeprowadzona, wszystkie istniejące tokeny są natychmiast unieważniane. Odpowiednio zaplanuj okna rotacji i poinformuj o nich konsumentów API.
- W architekturach mikroserwisów, gdzie wiele usług musi weryfikować tokeny, przełącz się na `RS256`. Wygeneruj parę kluczy RSA, przechowuj klucz prywatny w usłudze uwierzytelniania i dystrybuuj tylko klucz publiczny do usług konsumujących.
Krok 4: Skonfiguruj strażnika uwierzytelniania
Otwórz `config/auth.php` i zaktualizuj sekcje defaults i guards:
“`php
'defaults' => [
'guard' => 'api',
'passwords' => 'users',
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],
“`
Instruuje to system uwierzytelniania Laravel, aby używał sterownika JWT podczas rozwiązywania strażnika `api`. Middleware `auth:api` będzie teraz delegować walidację tokenów do `tymon/jwt-auth` zamiast Laravel Passport lub domyślnego sterownika tokenów.
Nie usuwaj strażnika `web`. Wiele wewnętrznych komponentów Laravel zależy od niego, a jego usunięcie powoduje nieoczekiwane błędy w poleceniach konsoli i pracownikach kolejek, które wchodzą w interakcję z systemem uwierzytelniania.
Krok 5: Utwórz model użytkownika i migrację
Jeśli domyślny model `User` i migracja już istnieją (tak jest w przypadku świeżej instalacji Laravel), możesz je modyfikować bezpośrednio. Jeśli zaczynasz od zera:
“`bash
php artisan make:model User -m
“`
Otwórz plik migracji w `database/migrations/` i zdefiniuj schemat:
“`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();
});
}
“`
Uruchom migrację:
“`bash
php artisan migrate
“`
Uwaga produkcyjna: Zawsze uruchamiaj migracje z flagą `–force` w środowiskach produkcyjnych, gdzie `APP_ENV=production`, ponieważ Laravel w przeciwnym razie wyświetli monit o potwierdzenie:
“`bash
php artisan migrate –force
“`
Krok 6: Zaimplementuj interfejs JWTSubject w modelu użytkownika
Pakiet `tymon/jwt-auth` wymaga, aby Twój model uwierzytelniający implementował kontrakt `JWTSubject`. Otwórz `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 [];
}
}
“`
O niestandardowych roszczeniach: Metoda `getJWTCustomClaims()` to miejsce, gdzie osadzasz dane specyficzne dla aplikacji bezpośrednio w ładunku tokenu. Typowe przypadki użycia obejmują osadzanie `role`, `tenant_id` lub `permissions`, aby usługi downstream mogły podejmować decyzje autoryzacyjne bez wyszukiwania w bazie danych. Bądź przemyślany w kwestii tego, co osadzasz — każde roszczenie zwiększa rozmiar tokenu i jest czytelne dla każdego, kto zdekoduje ładunek base64. Nigdy nie osadzaj wrażliwych danych, takich jak hasła lub dane osobowe.
Krok 7: Zbuduj kontroler uwierzytelniania
“`bash
php artisan make:controller AuthController
“`
Wypełnij `app/Http/Controllers/AuthController.php` pełną logiką uwierzytelniania:
“`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,
]);
}
}
“`
Dlaczego jawne określenie strażnika ma znaczenie: Wywołanie `Auth::attempt()` bez określenia strażnika powraca do domyślnego strażnika. Jeśli zmieniłeś domyślny na `api`, to działa — ale jest kruche. Zawsze wywołuj `Auth::guard('api')->attempt()` jawnie, aby uniknąć subtelnych błędów, gdy domyślny strażnik zmienia się podczas refaktoryzacji.
Krok 8: Zdefiniuj trasy API
Otwórz `routes/api.php` i zdefiniuj trasy uwierzytelniania i chronione:
“`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);
});
“`
Strategia prefiksu tras: Grupowanie punktów końcowych uwierzytelniania pod `/api/auth/` jest powszechnie przyjętą konwencją, która sprawia, że dokumentacja API jest bardziej przejrzysta i upraszcza reguły ograniczania szybkości — możesz stosować bardziej rygorystyczne ograniczanie do `/api/auth/login` niezależnie od punktów końcowych zasobów.
Krok 9: Ochrona tras i obsługa błędów middleware
Middleware `auth:api` Laravel zwróci odpowiedź `401 Unauthenticated`, gdy token jest brakujący, wygasły lub nieprawidłowy. Aby zwrócić spójną odpowiedź JSON zamiast przekierowania HTML, nadpisz metodę `unauthenticated` w `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'));
}
“`
Bez tego nadpisania klienci API otrzymują przekierowanie HTML 302 do strony logowania, która nie istnieje w czystej aplikacji API — częste źródło zamieszania podczas testowania integracji.
Krok 10: Strategia odświeżania tokenów i czarna lista
Bezstanowa natura JWT tworzy napięcie: tokeny są samowystarczalne i ważne do wygaśnięcia, ale potrzebujesz sposobu na ich unieważnienie przy wylogowaniu. Pakiet `tymon/jwt-auth` rozwiązuje to za pomocą czarnej listy tokenów opartej na sterowniku pamięci podręcznej Laravel.
Jak działa czarna lista:
- Przy wylogowaniu, roszczenie `jti` (JWT ID) tokenu jest przechowywane w pamięci podręcznej z TTL odpowiadającym pozostałemu czasowi życia tokenu.
- Przy każdym uwierzytelnionym żądaniu middleware sprawdza czarną listę przed zaakceptowaniem tokenu.
- Ustawienie `blacklist_grace_period` pozwala na krótkie okno, w którym token będący w trakcie odświeżania może być nadal używany, zapobiegając wyścigom w klientach wykonujących równoczesne żądania.
Upewnij się, że Twój sterownik pamięci podręcznej to obsługuje. Domyślny sterownik `file` działa dla wdrożeń na jednym serwerze. W przypadku poziomo skalowanych API działających na wielu węzłach — co jest powszechne przy używaniu Serwerów Dedykowanych w konfiguracji z równoważeniem obciążenia — przełącz się na Redis lub Memcached:
“`env
CACHE_DRIVER=redis
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
“`
Przepływ odświeżania tokenów:
“`
Client API Server
| — POST /api/auth/refresh ——> | |
| Authorization: Bearer <old> | |
| — Validate old token | |
| — Blacklist old token's jti | |
| — Issue new token | |
| <– 200 { token: <new> } ——– |
“`
Stary token jest natychmiast umieszczany na czarnej liście po odświeżeniu. Klienci muszą przechowywać nowy token i odrzucić stary. Zaimplementuj to jako interceptor Axios lub odpowiednik w swoim frontendzie, aby obsługiwać odświeżanie tokenów w sposób przezroczysty.
Krok 11: Testowanie API
Użyj `curl` lub Postman, aby zweryfikować każdy punkt końcowy.
Zarejestruj użytkownika:
“`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!"
}'
“`
Oczekiwana odpowiedź (`201 Created`):
“`json
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9…",
"token_type": "bearer",
"expires_in": 3600
}
“`
Zaloguj się:
“`bash
curl -X POST https://your-domain.com/api/auth/login
-H "Content-Type: application/json"
-d '{"email": "jane@example.com", "password": "SecurePass123!"}'
“`
Uzyskaj dostęp do chronionej trasy:
“`bash
curl -X GET https://your-domain.com/api/auth/me
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9…"
“`
Odśwież token:
“`bash
curl -X POST https://your-domain.com/api/auth/refresh
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9…"
“`
Wyloguj się:
“`bash
curl -X POST https://your-domain.com/api/auth/logout
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9…"
“`
JWT vs. Sesja vs. Laravel Sanctum vs. Passport
Zrozumienie, kiedy JWT jest właściwym wyborem, wymaga porównania go z alternatywami dostępnymi w ekosystemie Laravel.
| Kryterium | JWT (`tymon/jwt-auth`) | Laravel Sanctum | Laravel Passport | Oparte na sesji |
|---|---|---|---|---|
| — | — | — | — | — |
| **Stanowość** | Bezstanowe | Bezstanowe (tokeny SPA) / Stanowe (pliki cookie) | Stanowe (tokeny DB) | Stanowe |
| **Przechowywanie tokenów** | Po stronie klienta | Po stronie klienta lub plik cookie | Baza danych | Sesja po stronie serwera |
| **Unieważnianie** | Czarna lista (pamięć podręczna) | Natychmiastowe (usunięcie z DB) | Natychmiastowe (usunięcie z DB) | Natychmiastowe |
| **Skalowalność** | Doskonała (brak DB na żądanie) | Dobra | Umiarkowana (wyszukiwanie DB na żądanie) | Słaba (potrzebna synchronizacja sesji) |
| **Obsługa OAuth2** | Nie | Nie | Tak (pełny serwer OAuth2) | Nie |
| **Złożoność** | Średnia | Niska | Wysoka | Niska |
| **Najlepsze dla** | Bezstanowe API, mikroserwisy | SPA, aplikacje mobilne | Klienci OAuth innych firm | Tradycyjne aplikacje webowe |
| **Introspekcja tokenów** | Dekodowanie ładunku po stronie klienta | Wymaga wywołania API | Wymaga wywołania API | Nie dotyczy |
Kiedy wybrać JWT zamiast Sanctum: Jeśli Twoje API jest konsumowane przez klientów zewnętrznych, aplikacje mobilne lub mikroserwisy, które nie mogą współdzielić pliku cookie sesji lub połączenia z bazą danych z Twoją aplikacją Laravel, samowystarczalna natura JWT jest znaczącą zaletą. Jeśli budujesz własne SPA na tej samej domenie, Sanctum z uwierzytelnianiem opartym na plikach cookie jest prostszy i całkowicie eliminuje obawy dotyczące bezpieczeństwa przechowywania tokenów.
Utwardzanie bezpieczeństwa produkcyjnego
Działająca implementacja JWT nie jest domyślnie bezpieczna. Zastosuj te środki utwardzające przed uruchomieniem produkcyjnym:
1. Bezwarunkowo wymuszaj HTTPS
Tokeny JWT przesyłane przez HTTP są trywialnie przechwytywalne. Wymuszaj TLS na poziomie serwera webowego i przekierowuj cały ruch HTTP. Połącz to z Certyfikatem SSL, aby zapewnić szyfrowany transport dla każdego żądania API.
2. Ustaw agresywne TTL tokenów
Krótkotrwałe tokeny dostępu (15–30 minut) w połączeniu z dłużej żyjącymi tokenami odświeżania (7–14 dni) ograniczają zasięg skradzionego tokenu. Zaktualizuj `config/jwt.php`:
“`php
'ttl' => 15,
'refresh_ttl' => 10080,
“`
3. Zastosuj ograniczanie szybkości do punktów końcowych uwierzytelniania
Wbudowany middleware throttle Laravel zapobiega atakom brute-force:
“`php
Route::middleware(['throttle:10,1'])->group(function () {
Route::post('auth/login', [AuthController::class, 'login']);
Route::post('auth/register', [AuthController::class, 'register']);
});
“`
Ogranicza to każdy adres IP do 10 żądań na minutę na punktach końcowych uwierzytelniania.
4. Waliduj roszczenie `aud` dla API wielodostępnych
Jeśli Twoje API obsługuje wiele aplikacji klienckich, osadź i waliduj roszczenie `audience`, aby zapobiec ponownemu użyciu tokenów między usługami:
“`php
// In getJWTCustomClaims()
return [
'aud' => config('app.jwt_audience'),
];
“`
5. Chroń JWT_SECRET na poziomie systemu operacyjnego
Ustaw restrykcyjne uprawnienia do pliku `.env`:
“`bash
chmod 640 .env
chown www-data:www-data .env
“`
Na prawidłowo skonfigurowanym VPS z cPanel możesz zarządzać własnością plików i uprawnieniami przez Menedżer plików panelu lub SSH bez ryzyka ujawnienia kluczy tajnych innym użytkownikom systemu.
6. Rejestruj zdarzenia uwierzytelniania
Zintegruj system zdarzeń Laravel, aby rejestrować nieudane próby logowania, odświeżenia tokenów i wylogowania w scentralizowanej usłudze logowania. Jest to niezbędne do wykrywania anomalii.
Dodawanie kontroli dostępu opartej na rolach
Niestandardowe roszczenia ułatwiają osadzanie ról bezpośrednio w tokenie:
“`php
// In User model
public function getJWTCustomClaims(): array
{
return [
'role' => $this->role, // e.g., 'admin', 'editor', 'viewer'
];
}
“`
Utwórz middleware do egzekwowania wymagań dotyczących ról:
“`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);
}
}
“`
Zarejestruj go w `app/Http/Kernel.php` (Laravel 10) lub `bootstrap/app.php` (Laravel 11) i zastosuj do tras:
“`php
Route::middleware(['auth:api', 'role:admin'])->group(function () {
Route::apiResource('admin/users', AdminUserController::class);
});
“`
Zastrzeżenie: Dane ról osadzone w tokenie są aktualne tylko do momentu wygaśnięcia tokenu. Jeśli rola użytkownika zmieni się, stary token nadal przyznaje starą rolę do momentu wygaśnięcia lub odświeżenia. W przypadku zmian ról wymagających wysokiego bezpieczeństwa (np. natychmiastowe cofnięcie dostępu administratora) połącz umieszczanie tokenów na czarnej liście z krótkim TTL lub wykonaj sprawdzenie roli w bazie danych w middleware obok sprawdzenia roszczenia.
Uwagi dotyczące wdrożenia dla API Laravel JWT
Podczas przechodzenia z lokalnego środowiska deweloperskiego na serwer produkcyjny, kilka czynników specyficznych dla środowiska wpływa na zachowanie JWT:
- Spójność strefy czasowej: Roszczenia `iat`, `nbf` i `exp` JWT są znacznikami czasu Unix. Upewnij się, że strefa czasowa serwera jest ustawiona na UTC (`date.timezone = UTC` w `php.ini`), aby zapobiec odrzucaniu tokenów z powodu przesunięcia zegara.
- OPcache: Włącz PHP OPcache, aby zmniejszyć narzut związany z ładowaniem plików biblioteki JWT przy każdym żądaniu. Ma to szczególne znaczenie w przypadku API o dużym ruchu.
- Pracownicy kolejek do czyszczenia tokenów: Jeśli implementujesz niestandardowe zadania czyszczenia czarnej listy tokenów, upewnij się, że pracownik kolejki działa jako nadzorowany proces (Supervisor lub systemd).
- Zarządzanie zmiennymi środowiskowymi: Na Panelach sterowania VPS używaj menedżera zmiennych środowiskowych panelu zamiast bezpośredniego edytowania `.env` w produkcji, aby uniknąć przypadkowego nadpisania podczas wdrożeń.
Lista kontrolna decyzji przed uruchomieniem produkcyjnym
Użyj tej listy kontrolnej, aby sprawdzić, czy Twoja implementacja jest gotowa do produkcji:
- `JWT_SECRET` ma co najmniej 32 znaki, jest losowo wygenerowany i nie jest zacommitowany do kontroli wersji
- `blacklist_enabled` jest ustawiony na `true` w `config/jwt.php`
- TTL tokenu wynosi 30 minut lub mniej dla tokenów dostępu
- TTL odświeżania jest ustawiony na wartość odpowiednią dla Twojej polityki sesji
- Wszystkie punkty końcowe API są obsługiwane wyłącznie przez HTTPS
- Ograniczanie szybkości jest stosowane do punktów końcowych `/login` i `/register`
- Obsługa wyjątków `unauthenticated` zwraca JSON, a nie przekierowanie HTML
- Niestandardowe roszczenia nie zawierają haseł, kluczy tajnych ani wrażliwych danych osobowych
- Sterownik pamięci podręcznej to Redis lub Memcached (nie `file`) w wdrożeniach wieloserwerowych
- Zdarzenia uwierzytelniania są rejestrowane i monitorowane
- Zmiany ról wymagające natychmiastowego efektu są obsługiwane przez umieszczanie na czarnej liście, a nie tylko przez wygaśnięcie roszczenia
- Uprawnienia do pliku `.env` są ograniczone do użytkownika serwera webowego
FAQ
Jaka jest różnica między `JWT_SECRET` a `APP_KEY` w Laravel?
`APP_KEY` jest używany przez usługę szyfrowania Laravel do szyfrowania plików cookie, danych sesji i wartości przekazywanych przez `Crypt::encrypt()`. `JWT_SECRET` jest używany wyłącznie przez `tymon/jwt-auth` do podpisywania i weryfikacji JSON Web Tokenów. Są one kryptograficznie niezależne i służą zupełnie różnym celom. Oba muszą być utrzymywane w tajemnicy.
Dlaczego mój token JWT ciągle zwraca 401, mimo że nie wygasł?
Najczęstsze przyczyny to: token został umieszczony na czarnej liście (np. po wylogowaniu lub odświeżeniu), `JWT_SECRET` został zmieniony po wydaniu tokenu, sterownik pamięci podręcznej przechowujący czarną listę jest niedostępny lub istnieje przesunięcie zegara między serwerem wydającym a serwerem walidującym przekraczające ustawienie `leeway` w `config/jwt.php`. Sprawdź każdą z tych przyczyn po kolei.
Czy mogę używać uwierzytelniania JWT z kolejkami Laravel lub poleceniami konsoli?
JWT jest przeznaczony do uwierzytelniania żądań HTTP. Wewnątrz zadań kolejki lub poleceń Artisan nie ma kontekstu żądania HTTP, więc nie możesz rozwiązać użytkownika z tokenu przez middleware. Zamiast tego przekaż klucz główny użytkownika jako parametr zadania i załaduj model bezpośrednio za pomocą `User::find($userId)`.
Jak obsługiwać równoczesne żądania podczas odświeżania tokenu bez otrzymywania błędów 401?
Ustaw `blacklist_grace_period` w `config/jwt.php` na wartość między 10 a 30 sekund. W tym oknie token, który właśnie został odświeżony (i technicznie umieszczony na czarnej liście), będzie nadal akceptowany. Zapobiega to wyścigom w klientach, które wysyłają wiele równoczesnych żądań podczas trwającego odświeżania.
Czy `tymon/jwt-auth` jest kompatybilny z Laravel 11?
Według aktualnego cyklu wydań, `tymon/jwt-auth` wersja `2.x` obsługuje Laravel 10 i 11 przez gałąź `dev-develop` lub oznaczone wydania deklarujące kompatybilność. Zawsze sprawdzaj ograniczenia `composer.json` pakietu i tracker problemów GitHub przed aktualizacją wersji Laravel w projekcie zależnym od tego pakietu. Rozważ przypięcie wersji pakietu w `composer.json`, aby uniknąć nieoczekiwanych zmian łamiących kompatybilność podczas `composer update`.
