JWT-Tokens verstehen: Ein vollständiger Leitfaden
· 12 Min. Lesezeit
Inhaltsverzeichnis
- Was sind JSON Web Tokens (JWT)?
- Detaillierte JWT-Struktur und Anatomie
- JWTs in der Praxis erstellen und nutzen
- JWT-Authentifizierungs-Workflow erklärt
- Sicherheits-Best-Practices für JWT-Implementierung
- JWT vs. sitzungsbasierte Authentifizierung
- Vorteile von JWT in realen Anwendungen
- Häufige JWT-Fallstricke und wie man sie vermeidet
- Tools und Bibliotheken zur Verbesserung der JWT-Handhabung
- Testen und Debuggen von JWT-Implementierungen
- Häufig gestellte Fragen
- Verwandte Artikel
Was sind JSON Web Tokens (JWT)?
JSON Web Tokens (JWT) sind zum De-facto-Standard für die sichere Übertragung von Informationen zwischen Parteien in modernen Webanwendungen geworden. Definiert durch RFC 7519, bieten JWTs eine kompakte, URL-sichere Methode zur Darstellung von Claims, die zwischen zwei Parteien übertragen werden sollen.
Im Gegensatz zur traditionellen sitzungsbasierten Authentifizierung, bei der der Server den Zustand verwaltet, sind JWTs eigenständig. Das bedeutet, dass das Token selbst alle Informationen enthält, die zur Überprüfung der Identität und Berechtigungen eines Benutzers erforderlich sind. Diese zustandslose Natur macht JWTs besonders wertvoll in verteilten Systemen, Microservices-Architekturen und mobilen Anwendungen.
JWTs dienen zwei Hauptzwecken in modernen Anwendungen:
- Autorisierung: Sobald sich ein Benutzer anmeldet, enthält jede nachfolgende Anfrage das JWT, wodurch der Benutzer auf Routen, Dienste und Ressourcen zugreifen kann, die mit diesem Token erlaubt sind. Single-Sign-On (SSO)-Implementierungen verlassen sich aufgrund ihres geringen Overheads und ihrer domänenübergreifenden Fähigkeiten stark auf JWTs.
- Informationsaustausch: JWTs bieten eine sichere Möglichkeit, Informationen zwischen Parteien zu übertragen. Da JWTs mit öffentlichen/privaten Schlüsselpaaren signiert werden können, können Sie überprüfen, dass die Absender die sind, für die sie sich ausgeben, und dass der Inhalt nicht manipuliert wurde.
Die Schönheit von JWTs liegt in ihrer Einfachheit und Vielseitigkeit. Sie funktionieren nahtlos über verschiedene Programmiersprachen und Plattformen hinweg und sind daher ideal für heterogene Umgebungen, in denen Ihr Frontend in React, Ihr Backend in Node.js und Ihre mobile App in Swift oder Kotlin sein könnte.
Profi-Tipp: Obwohl JWTs unglaublich nützlich sind, sind sie keine Universallösung für alle Authentifizierungsszenarien. Zu verstehen, wann JWTs gegenüber traditionellen Sitzungen verwendet werden sollten, ist entscheidend für den Aufbau sicherer, skalierbarer Anwendungen.
Detaillierte JWT-Struktur und Anatomie
Ein JWT besteht aus drei verschiedenen Teilen, die durch Punkte (.) getrennt sind und eine Struktur bilden, die so aussieht: xxxxx.yyyyy.zzzzz. Jeder Abschnitt dient einem bestimmten Zweck und zusammen bilden sie ein manipulationssicheres Informationspaket.
Der Header
Der Header besteht typischerweise aus zwei Teilen: dem Typ des Tokens (JWT) und dem verwendeten Signaturalgorithmus, wie HMAC SHA256 oder RSA. Diese Information teilt der empfangenden Partei mit, wie die Signatur des Tokens validiert werden soll.
{
"alg": "HS256",
"typ": "JWT"
}
Der Header wird dann Base64Url-kodiert, um den ersten Teil des JWT zu bilden. Häufige Algorithmen, denen Sie begegnen werden, sind:
- HS256 (HMAC mit SHA-256): Symmetrischer Algorithmus mit einem gemeinsamen Geheimnis
- RS256 (RSA-Signatur mit SHA-256): Asymmetrischer Algorithmus mit öffentlichen/privaten Schlüsselpaaren
- ES256 (ECDSA mit SHA-256): Asymmetrischer Algorithmus mit elliptischer Kurvenkryptographie
Die Payload
Die Payload enthält die Claims, die Aussagen über eine Entität (typischerweise den Benutzer) und zusätzliche Metadaten sind. Claims werden in drei Typen kategorisiert:
Registrierte Claims: Dies sind vordefinierte Claims, die nicht obligatorisch, aber zur Gewährleistung der Interoperabilität empfohlen sind. Sie umfassen:
iss(issuer): Identifiziert, wer das Token ausgestellt hatsub(subject): Identifiziert das Subjekt des Tokens (normalerweise die Benutzer-ID)aud(audience): Identifiziert die Empfänger, für die das JWT bestimmt istexp(expiration time): Zeitstempel, nach dem das JWT abläuftnbf(not before): Zeitstempel, vor dem das JWT nicht akzeptiert werden darfiat(issued at): Zeitstempel, wann das JWT ausgestellt wurdejti(JWT ID): Eindeutige Kennung für das JWT
Öffentliche Claims: Dies sind benutzerdefinierte Claims, die Sie für Ihre Anwendung definieren. Um Kollisionen zu vermeiden, sollten sie im IANA JSON Web Token Registry definiert oder kollisionsresistente Namen (wie Namespace-URIs) verwenden.
Private Claims: Benutzerdefinierte Claims, die erstellt wurden, um Informationen zwischen Parteien auszutauschen, die sich auf ihre Verwendung einigen. Dies sind weder registrierte noch öffentliche Claims.
{
"sub": "1234567890",
"name": "John Doe",
"email": "[email protected]",
"role": "admin",
"iat": 1516239022,
"exp": 1516242622
}
Die Payload wird dann Base64Url-kodiert, um den zweiten Teil des JWT zu bilden.
Schneller Tipp: Speichern Sie niemals sensible Informationen wie Passwörter oder Kreditkartennummern in JWT-Payloads. Obwohl JWTs signiert sind, sind sie standardmäßig nicht verschlüsselt, was bedeutet, dass jeder die Payload dekodieren und lesen kann.
Die Signatur
Die Signatur wird erstellt, indem der kodierte Header, die kodierte Payload, ein Geheimnis (für HMAC-Algorithmen) oder ein privater Schlüssel (für RSA/ECDSA) genommen und mit dem im Header angegebenen Algorithmus signiert werden.
Wenn beispielsweise HMAC SHA256 verwendet wird, würde die Signatur so erstellt:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
Die Signatur stellt sicher, dass das Token nicht verändert wurde. Wenn jemand auch nur ein einzelnes Zeichen im Header oder in der Payload ändert, schlägt die Signaturüberprüfung fehl.
Vollständiges JWT-Beispiel
Wenn Sie alle drei Teile zusammenfügen, erhalten Sie ein vollständiges JWT, das so aussieht:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Sie können jedes JWT mit unserem JWT-Decoder-Tool dekodieren und inspizieren, um seinen Header, seine Payload zu sehen und seine Signatur zu überprüfen.
JWTs in der Praxis erstellen und nutzen
Das Erstellen von JWTs ist mit den richtigen Bibliotheken unkompliziert. Lassen Sie uns untersuchen, wie JWTs über verschiedene Programmiersprachen und Szenarien hinweg generiert und verwendet werden.
JWTs in Node.js erstellen
Die jsonwebtoken-Bibliothek ist die beliebteste Wahl für Node.js-Anwendungen:
const jwt = require('jsonwebtoken');
// Token erstellen
const payload = {
sub: user.id,
email: user.email,
role: user.role
};
const secret = process.env.JWT_SECRET;
const options = {
expiresIn: '1h',
issuer: 'myapp.com'
};
const token = jwt.sign(payload, secret, options);
// Token verifizieren
try {
const decoded = jwt.verify(token, secret);
console.log(decoded);
} catch (error) {
console.error('Ungültiges Token:', error.message);
}
JWTs in Python erstellen
Python-Entwickler verwenden typischerweise die PyJWT-Bibliothek:
import jwt
import datetime
# Token erstellen
payload = {
'sub': user.id,
'email': user.email,
'role': user.role,
'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=1),
'iat': datetime.datetime.utcnow()
}
secret = os.environ.get('JWT_SECRET')
token = jwt.encode(payload, secret, algorithm='HS256')
# Token verifizieren
try:
decoded = jwt.decode(token, secret, algorithms=['HS256'])
print(decoded)
except jwt.ExpiredSignatureError:
print('Token ist abgelaufen')
except jwt.InvalidTokenError:
print('Ungültiges Token')
JWTs in Java erstellen
Für Java-Anwendungen wird die java-jwt-Bibliothek von Auth0 häufig verwendet:
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
// Token erstellen
Algorithm algorithm = Algorithm.HMAC256(secret);
String token = JWT.create()
.withSubject(user.getId())
.withClaim("email", user.getEmail())
.withClaim("role", user.getRole())
.withExpiresAt(new Date(System.currentTimeMillis() + 3600000))
.withIssuer("myapp.com")
.sign(algorithm);
// Token verifizieren
try {
DecodedJWT jwt = JWT.require(algorithm)
.withIssuer("myapp.com")
.build()
.verify(token);
} catch (Exception e) {
System.out.println("Ungültiges Token: " + e.getMessage());
}
Best Practices für die Token-Generierung
Befolgen Sie beim Erstellen von JWTs diese Richtlinien, um Sicherheit und Zuverlässigkeit zu gewährleisten:
- Verwenden Sie starke Geheimnisse: Ihr JWT-Geheimnis sollte mindestens 256 Bit (32 Zeichen) an Zufallsdaten sein. Codieren Sie niemals Geheimnisse fest in Ihren Quellcode.
- Setzen Sie angemessene Ablaufzeiten: Zugriffstokens sollten kurzlebig sein (15 Minuten bis 1 Stunde), während Refresh-Tokens länger halten können (Tage bis Wochen).
- Fügen Sie nur notwendige Claims hinzu: Halten Sie Ihre Payload schlank, um die Token-Größe zu reduzieren und die Informationsoffenlegung zu minimieren.
- Verwenden Sie den richtigen Algorithmus: Für die meisten Anwendungen ist HS256 ausreichend. Verwenden Sie RS256, wenn Sie Tokens ohne gemeinsame Geheimnisse verifizieren müssen (wie in Microservices).
Profi-Tipp: Erwägen Sie die Implementierung eines Token-Refresh-Mechanismus mit Refresh-Tokens. Dies ermöglicht es Ihnen, Zugriffstokens aus Sicherheitsgründen kurzlebig zu halten und gleichzeitig eine gute Benutzererfahrung ohne häufige erneute Authentifizierung aufrechtzuerhalten.
JWT-Authentifizierungs-Workflow erklärt
Zu verstehen, wie JWTs durch Ihre Anwendung fließen, ist entscheidend für ihre korrekte Implementierung. Lassen Sie uns Schritt für Schritt durch einen typischen Authentifizierungs-Workflow gehen.
Initiale Authentifizierung
- Benutzer übermittelt Anmeldedaten: Der Benutzer sendet seinen Benutzernamen und sein Passwort an den Authentifizierungs-Endpunkt (typischerweise
POST /api/auth/login). - Server validiert Anmeldedaten: Der Server überprüft die Anmeldedaten gegen die Datenbank und verifiziert, dass der Passwort-Hash übereinstimmt.
- Server generiert JWT: Bei erfolgreicher Validierung erstellt der Server ein JWT, das die Identität des Benutzers und relevante Claims enthält.
- Server gibt Token zurück: Das JWT wird an den Client zurückgesendet, typischerweise im Response-Body. Einige Implementierungen setzen es auch als HTTP-only-Cookie.
Nachfolgende Anfragen
- Client fügt Token hinzu: Für jede nachfolgende Anfrage fügt der Client das JWT im Authorization-Header hinzu:
Authorization: Bearer <token> - Server validiert Token: Der Server extrahiert das Token, verifiziert seine Signatur und überprüft sein Ablaufdatum.
- Server verarbeitet Anfrage: Wenn das Token gültig ist, extrahiert der Server die Benutzerinformationen aus der Payload und verarbeitet die Anfrage entsprechend.
- Server gibt Antwort zurück: Die angeforderten Daten oder das Operationsergebnis werden an den Client zurückgegeben.
Token-Refresh-Flow
Wenn das Zugriffstoken abläuft, wird der Refresh-Flow aktiviert:
- Client erkennt Ablauf: Der Client erhält eine 401-Unauthorized-Antwort oder überprüft den
exp-Claim des Tokens. - Client sendet Refresh-Token: Der Client sendet das Refresh-Token an einen dedizierten Refresh-Endpunkt (
POST /api/auth/refresh). - Server validiert Refresh-Token: Der Server verifiziert das Refresh-Token und prüft, ob es widerrufen wurde.
- Server stellt neues Zugriffstoken aus: Ein neues Zugriffstoken wird generiert und an den Client zurückgegeben.
- Client wiederholt ursprüngliche Anfrage: Der Client verwendet das neue Zugriffstoken, um die fehlgeschlagene Anfrage zu wiederholen.
Logout-Flow
Das Abmelden mit JWTs erfordert besondere Überlegungen, da Tokens zustandslos sind:
- Client initiiert Logout: Der Benutzer klickt auf Abmelden, was eine Anfrage an
POST /api/auth/logoutauslöst. - Server invalidiert Refresh-Token: Der Server fügt das Refresh-Token einer Widerrufsliste hinzu oder löscht es aus der Datenbank.
- Client verwirft Tokens: Der Client entfernt sowohl Zugriffs- als auch Refresh-Tokens aus dem Speicher.
- Client leitet um: Der Benutzer wird zur Anmeldeseite oder zum öffentlichen Bereich weitergeleitet.
| Token-Typ | Typische Lebensdauer | Speicherort | Zweck |
|---|---|---|---|
| Zugriffstoken | 15 Min. - 1 Stunde | Speicher oder sessionStorage | API-Anfragen autorisieren |
| Refresh-Token | 7 Tage - 30 Tage | HTTP-only-Cookie oder sicherer Speicher | Neue Zugriffstokens erhalten |
| ID-Token | Gleich wie Zugriffstoken | Speicher | Benutzeridentitätsinformationen (OpenID Connect) |
Schneller Tipp: Implementieren Sie automatisches Token-Refresh in Ihrer Client-Anwendung, um den Token-Ablauf nahtlos zu handhaben. Dies verhindert, dass Benutzer unerwartet abgemeldet