JSON Web Tokens (JWT): Kompletter Leitfaden für sichere Authentifizierung
Lernen Sie alles über JSON Web Tokens (JWT). Struktur, Signierung, Validierung und Best Practices für sichere Authentifizierung in modernen Anwendungen.
Big JSON Team
• Technical WriterExpert in JSON data manipulation, API development, and web technologies. Passionate about creating tools that make developers' lives easier.
# JSON Web Tokens (JWT): Kompletter Leitfaden
JSON Web Tokens (JWT) sind der de-facto Standard für sichere Authentifizierung in modernen Web-Anwendungen. Dieser Leitfaden erklärt alles, was Sie wissen müssen.
Was ist ein JWT?
Ein JWT ist ein kompakter, URL-sicherer Token, der verwendet wird, um Informationen zwischen zwei Parteien sicher zu übertragen.
Beispiel eines JWT
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Ik1heCBNdXN0ZXJtYW5uIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
JWT-Struktur
Ein JWT besteht aus drei Teilen, getrennt durch Punkte (.):
HEADER.PAYLOAD.SIGNATURE
1. Header
Der Header enthält Metadaten über den Token:
{
"alg": "HS256",
"typ": "JWT"
}
- alg: Algorithmus (z.B. HS256, RS256)
- typ: Token-Typ (immer JWT)
Base64URL-kodiert: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
2. Payload
Der Payload enthält die Claims (Behauptungen):
{
"sub": "1234567890",
"name": "Max Mustermann",
"email": "max@example.com",
"iat": 1516239022,
"exp": 1516242622
}
Base64URL-kodiert: eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Ik1heCBNdXN0ZXJtYW5uIiwiaWF0IjoxNTE2MjM5MDIyfQ
3. Signature
Die Signatur stellt sicher, dass der Token nicht manipuliert wurde:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
Standard Claims
Registered Claims
iss (Issuer) - Aussteller des Tokens{"iss": "https://auth.example.com"}
sub (Subject) - Subjekt des Tokens (meist User-ID)
{"sub": "user123"}
aud (Audience) - Empfänger des Tokens
{"aud": "https://api.example.com"}
exp (Expiration Time) - Ablaufzeit (Unix Timestamp)
{"exp": 1706265600}
nbf (Not Before) - Gültig ab (Unix Timestamp)
{"nbf": 1706262000}
iat (Issued At) - Ausstellungszeit
{"iat": 1706262000}
jti (JWT ID) - Eindeutige Token-ID
{"jti": "abc123xyz"}
Custom Claims
Sie können eigene Claims hinzufügen:
{
"sub": "user123",
"name": "Max Mustermann",
"email": "max@example.com",
"roles": ["admin", "user"],
"department": "IT",
"customData": {
"preference": "dark-mode"
}
}
JWT erstellen und validieren
Node.js mit jsonwebtoken
const jwt = require('jsonwebtoken');
// Secret Key (sollte in Umgebungsvariablen gespeichert werden)
const SECRET_KEY = process.env.JWT_SECRET || 'your-secret-key';
// JWT erstellen
function createToken(userId, email) {
const payload = {
sub: userId,
email: email,
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + (60 * 60) // 1 Stunde
};
return jwt.sign(payload, SECRET_KEY, {
algorithm: 'HS256'
});
}
// JWT validieren
function verifyToken(token) {
try {
const decoded = jwt.verify(token, SECRET_KEY);
return {
valid: true,
data: decoded
};
} catch (error) {
return {
valid: false,
error: error.message
};
}
}
// Verwendung
const token = createToken('user123', 'max@example.com');
console.log('Token:', token);
const result = verifyToken(token);
if (result.valid) {
console.log('Token ist gültig:', result.data);
} else {
console.log('Token ungültig:', result.error);
}
Python mit PyJWT
import jwt
import datetime
import os
SECRET_KEY = os.getenv('JWT_SECRET', 'your-secret-key')
def create_token(user_id: str, email: str) -> str:
"""Erstelle JWT"""
payload = {
'sub': user_id,
'email': email,
'iat': datetime.datetime.utcnow(),
'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=1)
}
return jwt.encode(payload, SECRET_KEY, algorithm='HS256')
def verify_token(token: str) -> dict:
"""Validiere JWT"""
try:
decoded = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
return {'valid': True, 'data': decoded}
except jwt.ExpiredSignatureError:
return {'valid': False, 'error': 'Token abgelaufen'}
except jwt.InvalidTokenError as e:
return {'valid': False, 'error': str(e)}
# Verwendung
token = create_token('user123', 'max@example.com')
print(f"Token: {token}")
result = verify_token(token)
if result['valid']:
print(f"Token gültig: {result['data']}")
else:
print(f"Token ungültig: {result['error']}")
Signatur-Algorithmen
Symmetric (HMAC)
HS256, HS384, HS512 - Shared Secret// Gleicher Secret für Signierung und Validierung
const token = jwt.sign(payload, 'secret-key', { algorithm: 'HS256' });
const decoded = jwt.verify(token, 'secret-key');
✅ Vorteile:
- Schnell
- Einfach zu implementieren
⚠️ Nachteile:
- Secret muss geteilt werden
- Nur geeignet für vertrauenswürdige Umgebungen
Asymmetric (RSA/ECDSA)
RS256, RS384, RS512 - RSA ES256, ES384, ES512 - ECDSAconst fs = require('fs');
// Private Key für Signierung
const privateKey = fs.readFileSync('private.key');
const token = jwt.sign(payload, privateKey, { algorithm: 'RS256' });
// Public Key für Validierung
const publicKey = fs.readFileSync('public.key');
const decoded = jwt.verify(token, publicKey);
✅ Vorteile:
- Public Key kann sicher geteilt werden
- Besser für verteilte Systeme
⚠️ Nachteile:
- Langsamer als HMAC
- Komplexere Key-Verwaltung
Praktische Implementierung
Express.js Middleware
const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();
const SECRET_KEY = process.env.JWT_SECRET;
// Middleware für JWT-Authentifizierung
function authenticateToken(req, res, next) {
// Token aus Header extrahieren
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1]; // "Bearer TOKEN"
if (!token) {
return res.status(401).json({ error: 'Kein Token bereitgestellt' });
}
jwt.verify(token, SECRET_KEY, (err, user) => {
if (err) {
return res.status(403).json({ error: 'Token ungültig oder abgelaufen' });
}
req.user = user;
next();
});
}
// Login-Route
app.post('/api/login', (req, res) => {
const { username, password } = req.body;
// Benutzer authentifizieren (Pseudo-Code)
const user = authenticateUser(username, password);
if (!user) {
return res.status(401).json({ error: 'Ungültige Anmeldedaten' });
}
// JWT erstellen
const token = jwt.sign(
{
sub: user.id,
username: user.username,
roles: user.roles
},
SECRET_KEY,
{ expiresIn: '1h' }
);
res.json({ token });
});
// Geschützte Route
app.get('/api/profile', authenticateToken, (req, res) => {
// req.user enthält die dekodierten Token-Daten
res.json({
message: 'Geschützte Daten',
user: req.user
});
});
app.listen(3000);
FastAPI (Python)
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
import jwt
from datetime import datetime, timedelta
from pydantic import BaseModel
app = FastAPI()
security = HTTPBearer()
SECRET_KEY = "your-secret-key"
class LoginRequest(BaseModel):
username: str
password: str
class TokenResponse(BaseModel):
access_token: str
token_type: str = "bearer"
def create_access_token(data: dict, expires_delta: timedelta = None):
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(hours=1)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm="HS256")
return encoded_jwt
def verify_token(credentials: HTTPAuthorizationCredentials = Depends(security)):
try:
payload = jwt.decode(
credentials.credentials,
SECRET_KEY,
algorithms=["HS256"]
)
return payload
except jwt.ExpiredSignatureError:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Token abgelaufen"
)
except jwt.InvalidTokenError:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Ungültiger Token"
)
@app.post("/api/login", response_model=TokenResponse)
async def login(request: LoginRequest):
# Benutzer authentifizieren (Pseudo-Code)
if not authenticate_user(request.username, request.password):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Ungültige Anmeldedaten"
)
token = create_access_token(
data={"sub": request.username},
expires_delta=timedelta(hours=1)
)
return TokenResponse(access_token=token)
@app.get("/api/profile")
async def get_profile(user_data = Depends(verify_token)):
return {
"message": "Geschützte Daten",
"user": user_data
}
Refresh Tokens
Access Token + Refresh Token Pattern
const jwt = require('jsonwebtoken');
const ACCESS_TOKEN_SECRET = process.env.ACCESS_TOKEN_SECRET;
const REFRESH_TOKEN_SECRET = process.env.REFRESH_TOKEN_SECRET;
function generateTokens(userId) {
// Kurzlebiger Access Token (15 Minuten)
const accessToken = jwt.sign(
{ sub: userId },
ACCESS_TOKEN_SECRET,
{ expiresIn: '15m' }
);
// Langlebiger Refresh Token (7 Tage)
const refreshToken = jwt.sign(
{ sub: userId },
REFRESH_TOKEN_SECRET,
{ expiresIn: '7d' }
);
return { accessToken, refreshToken };
}
// Refresh-Endpoint
app.post('/api/token/refresh', (req, res) => {
const { refreshToken } = req.body;
if (!refreshToken) {
return res.status(401).json({ error: 'Refresh Token erforderlich' });
}
try {
const decoded = jwt.verify(refreshToken, REFRESH_TOKEN_SECRET);
// Neuen Access Token generieren
const newAccessToken = jwt.sign(
{ sub: decoded.sub },
ACCESS_TOKEN_SECRET,
{ expiresIn: '15m' }
);
res.json({ accessToken: newAccessToken });
} catch (error) {
return res.status(403).json({ error: 'Ungültiger Refresh Token' });
}
});
Sicherheits-Best Practices
1. Sichere Secret Keys
// SCHLECHT - Hardcoded Secret
const SECRET = 'my-secret';
// GUT - Umgebungsvariable
const SECRET = process.env.JWT_SECRET;
// Generiere starken Secret:
// node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
2. Token-Ablaufzeit setzen
// Kurze Lebenszeit für Access Tokens
jwt.sign(payload, secret, { expiresIn: '15m' });
// Längere Lebenszeit für Refresh Tokens
jwt.sign(payload, secret, { expiresIn: '7d' });
3. HTTPS verwenden
// Immer über HTTPS übertragen
https://api.example.com/login
4. Sensitive Daten nicht im Payload
// SCHLECHT
const payload = {
sub: userId,
password: 'secret123', // ❌ NIEMALS!
creditCard: '1234-5678' // ❌ NIEMALS!
};
// GUT
const payload = {
sub: userId,
roles: ['user'],
email: 'user@example.com'
};
5. Token-Widerrufung implementieren
const redis = require('redis');
const client = redis.createClient();
// Token zur Blacklist hinzufügen
async function revokeToken(token) {
const decoded = jwt.decode(token);
const expiresIn = decoded.exp - Math.floor(Date.now() / 1000);
await client.setex(blacklist:\\${token}, expiresIn, 'revoked');
}
// Token-Blacklist prüfen
async function isTokenRevoked(token) {
const result = await client.get(blacklist:\\${token});
return result !== null;
}
// Middleware anpassen
async function authenticateToken(req, res, next) {
const token = extractToken(req);
if (await isTokenRevoked(token)) {
return res.status(403).json({ error: 'Token widerrufen' });
}
// ... normale Validierung
}
Debugging und Testing
JWT dekodieren (ohne Validierung)
const jwt = require('jsonwebtoken');
const token = 'eyJhbGc...';
// Dekodieren ohne Validierung
const decoded = jwt.decode(token, { complete: true });
console.log('Header:', decoded.header);
console.log('Payload:', decoded.payload);
console.log('Signature:', decoded.signature);
Online-Tools
- jwt.io - JWT debuggen und testen
- jwt-cli - Kommandozeilen-Tool
Unit-Tests
const jwt = require('jsonwebtoken');
const { expect } = require('chai');
describe('JWT Tests', () => {
const SECRET = 'test-secret';
it('sollte gültigen Token erstellen', () => {
const payload = { sub: 'user123' };
const token = jwt.sign(payload, SECRET);
expect(token).to.be.a('string');
expect(token.split('.')).to.have.lengthOf(3);
});
it('sollte Token validieren', () => {
const payload = { sub: 'user123' };
const token = jwt.sign(payload, SECRET);
const decoded = jwt.verify(token, SECRET);
expect(decoded.sub).to.equal('user123');
});
it('sollte abgelaufenen Token ablehnen', (done) => {
const payload = { sub: 'user123' };
const token = jwt.sign(payload, SECRET, { expiresIn: '1ms' });
setTimeout(() => {
expect(() => jwt.verify(token, SECRET)).to.throw();
done();
}, 10);
});
});
Zusammenfassung
JWT ist ein leistungsstarkes Tool für Authentifizierung, wenn es richtig eingesetzt wird:
✅ Verwenden Sie:
- Starke, zufällige Secret Keys
- Kurze Ablaufzeiten für Access Tokens
- HTTPS für alle Token-Übertragungen
- Refresh Token Pattern
- Token-Blacklisting für Logout
❌ Vermeiden Sie:
- Sensitive Daten im Payload
- Lange Ablaufzeiten
- Schwache Signatur-Algorithmen
- Client-seitiges Speichern in localStorage (verwenden Sie httpOnly Cookies)
Mit diesen Best Practices können Sie JWT sicher in Ihren Anwendungen einsetzen.
Verwandte Artikel
Was ist JSON? Vollständiger Leitfaden für Anfänger
Lernen Sie die Definition, Geschichte und Struktur von JSON. Vollständiger Leitfaden für Anfänger mit Datentypen, Syntaxregeln und praktischen Beispielen.
JavaScript und JSON: Vollständige Anleitung für Web-Entwickler
Meistern Sie JSON in JavaScript: Parsen, Stringifyen, Manipulation, Best Practices und praktische Beispiele für moderne Webentwicklung.
JSON APIs und REST-Dienste: Vollständiger Entwickler-Leitfaden
Meistern Sie JSON in REST-APIs: Best Practices, Beispiele, Authentifizierung, Fehlerbehandlung und moderne API-Entwicklung.