JWT - idea i zastosowanie
Wprowadzenie
Dział zatytułowany „Wprowadzenie”JWT (JSON Web Token) to otwarty standard (RFC 7519) definiujacy kompaktowy i samodzielny sposób bezpiecznego przekazywania informacji miedzy stronami jako obiekt JSON. Token może być zweryfikowany i zaufany, ponieważ jest cyfrowo podpisany.
JWT stal się de facto standardem autentykacji w nowoczesnych aplikacjach webowych i API, szczegolnie w architekturach stateless i mikroserwisowych.
Kluczowe pojecia
Dział zatytułowany „Kluczowe pojecia”Struktura JWT
Dział zatytułowany „Struktura JWT”JWT składa się z trzech części oddzielonych kropkami:
xxxxx.yyyyy.zzzzz │ │ │ │ │ └── Signature (podpis) │ └──────── Payload (dane) └─────────────── Header (nagłówek)Header (Nagłówek)
Dział zatytułowany „Header (Nagłówek)”Zawiera metadane tokena - typ tokena i algorytm podpisu:
{ "alg": "HS256", "typ": "JWT"}Payload (Dane)
Dział zatytułowany „Payload (Dane)”Zawiera claims - twierdzenia o podmiocie (uzytkowniku) i dodatkowe dane:
{ "sub": "1234567890", "name": "Jan Kowalski", "email": "jan@example.com", "role": "admin", "iat": 1516239022, "exp": 1516242622}Standardowe claims:
sub(subject) - identyfikator podmiotuiat(issued at) - czas wydania tokenaexp(expiration) - czas wygasnieciaiss(issuer) - wydawca tokenaaud(audience) - odbiorca tokena
Signature (Podpis)
Dział zatytułowany „Signature (Podpis)”Podpis kryptograficzny gwarantujacy integralnosc tokena:
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)Jak to działa?
Dział zatytułowany „Jak to działa?”- Użytkownik loguje się - wysyła login i hasło do serwera
- Serwer weryfikuje dane - sprawdza użytkownika w bazie
- Serwer generuje JWT - tworzy token z danymi użytkownika
- Klient otrzymuje token - przechowuje go (localStorage, cookie)
- Klient wysyła token - dołącza do każdego zadania w nagłówku
- Serwer weryfikuje token - sprawdza podpis i waznosc
- Serwer autoryzuje zadanie - na podstawie danych z tokena
Diagram przepływu
Dział zatytułowany „Diagram przepływu”┌────────┐ ┌────────┐│ Klient │ │ Serwer │└───┬────┘ └───┬────┘ │ │ │ 1. POST /login {email, hasło} │ │ ────────────────────────────────>│ │ │ │ 2. Weryfikacja + generowanie JWT │ │ <─────────────────────────────── │ │ {token: "eyJhbG..."} │ │ │ │ 3. GET /api/profile │ │ Authorization: Bearer eyJhbG..│ │ ────────────────────────────────>│ │ │ │ 4. Weryfikacja podpisu JWT │ │ <─────────────────────────────── │ │ {user: {...}} │Przykłady w kodzie
Dział zatytułowany „Przykłady w kodzie”Przykładowy token JWT
Dział zatytułowany „Przykładowy token JWT”eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkphbiBLb3dhbHNraSIsInJvbGUiOiJ1c2VyIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE1MTYyNDI2MjJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5cGenerowanie JWT (Node.js)
Dział zatytułowany „Generowanie JWT (Node.js)”import jwt from 'jsonwebtoken';
const SECRET_KEY = process.env.JWT_SECRET;
function generateToken(user: User): string { const payload = { sub: user.id, email: user.email, role: user.role };
const options = { expiresIn: '1h', // Token wygasa po 1 godzinie issuer: 'moja-aplikacja' };
return jwt.sign(payload, SECRET_KEY, options);}
// Użycieconst token = generateToken({ id: '123', email: 'jan@example.com', role: 'user' });console.log(token);Weryfikacja JWT (Node.js)
Dział zatytułowany „Weryfikacja JWT (Node.js)”import jwt from 'jsonwebtoken';
function verifyToken(token: string): JwtPayload | null { try { const decoded = jwt.verify(token, SECRET_KEY); return decoded as JwtPayload; } catch (error) { if (error instanceof jwt.TokenExpiredError) { console.log('Token wygasł'); } else if (error instanceof jwt.JsonWebTokenError) { console.log('Nieprawidłowy token'); } return null; }}
// Middleware Express.jsfunction authMiddleware(req, res, next) { const authHeader = req.headers.authorization;
if (!authHeader?.startsWith('Bearer ')) { return res.status(401).json({ error: 'Brak tokena' }); }
const token = authHeader.split(' ')[1]; const payload = verifyToken(token);
if (!payload) { return res.status(401).json({ error: 'Nieprawidłowy token' }); }
req.user = payload; next();}Dekodowanie payloadu (bez weryfikacji)
Dział zatytułowany „Dekodowanie payloadu (bez weryfikacji)”// UWAGA: To NIE weryfikuje podpisu!function decodePayload(token: string): object { const parts = token.split('.'); const payload = parts[1]; const decoded = Buffer.from(payload, 'base64url').toString('utf8'); return JSON.parse(decoded);}
// Przykładconst data = decodePayload(token);console.log(data);// { sub: "123", email: "jan@example.com", role: "user", iat: ..., exp: ... }Czy JWT jest szyfrowany?
Dział zatytułowany „Czy JWT jest szyfrowany?”Co to oznacza w praktyce:
- Nigdy nie przechowuj w JWT haseł, tokenow API, danych wrazliwych
- Uzywaj HTTPS do transmisji tokenow
- Jeśli potrzebujesz szyfrowania, użyj JWE (JSON Web Encryption)
Algorytmy podpisu
Dział zatytułowany „Algorytmy podpisu”| Algorytm | Typ | Opis |
|---|---|---|
| HS256 | Symetryczny | Jeden tajny klucz dla podpisu i weryfikacji |
| RS256 | Asymetryczny | Klucz prywatny do podpisu, publiczny do weryfikacji |
| ES256 | Asymetryczny | ECDSA - krotsza sygnatura, ta sama siła |
Rekomendacja: RS256 dla systemow z wieloma serwisami (każdy ma klucz publiczny), HS256 dla prostszych aplikacji.
Najczestsze błędy
Dział zatytułowany „Najczestsze błędy”Dobre praktyki
Dział zatytułowany „Dobre praktyki”- Krótki czas waznosci - access token: 15 min, refresh token: 7 dni
- Minimalna ilosc danych - tylko niezbedne claims
- Bezpieczne przechowywanie - httpOnly cookie lub secure storage
- Rotacja kluczy - okresowa zmiana secret/kluczy
- Blacklisting - możliwość unieważniania tokenow przed exp
- Walidacja algorytmu - nie akceptuj “alg”: “none”
Podsumowanie
Dział zatytułowany „Podsumowanie”- JWT to samodzielny token zawierajacy dane użytkownika i podpis
- Składa się z trzech części: Header, Payload, Signature
- Jest zakodowany (base64url), ale nie zaszyfrowany
- Podpis gwarantuje integralnosc, nie poufnosc
- Idealny do stateless authentication w API i mikroserwisach
- Wymaga ostroznosci - nie przechowuj danych wrazliwych w payload