Einführung in JWT
JWT (JSON Web Token) ist eine knappe, eigenständige Methode zur Authentifizierung im Web. Es besteht aus drei Teilen: dem Header, dem Payload und der Signatur. Der Header enthält Informationen zum Typ des Tokens und zum Verschlüsselungsalgorithmus, der Payload enthält die zu übermittelnden Daten (in der Regel einschließlich einiger Berechtigungsinformationen und Benutzeridentifikation), und die Signatur wird zur Überprüfung der Integrität und Gültigkeit des Tokens verwendet.
JWT wird hauptsächlich in zwei Szenarien verwendet:
- Authentifizierung: Sobald sich ein Benutzer anmeldet, wird bei jeder nachfolgenden Anfrage ein JWT mitgeschickt, um dem Benutzer den Zugriff auf erlaubte Routen, Dienste und Ressourcen zu ermöglichen.
- Informationsaustausch: JWT ist eine gute Methode, um Informationen sicher zwischen Parteien zu übertragen, da sie digital signiert werden können, beispielsweise mithilfe von öffentlichen/privaten Schlüsselpaaren.
Datenformat von JWT
JWT ist eine kompakte und URL-sichere Methode zur Darstellung von Informationen, die zwischen Parteien ausgetauscht werden sollen. Ein JWT besteht tatsächlich aus drei Teilen, die durch Punkte (.``) getrennt sind, nämlich
Header.Payload.Signature`. Im Folgenden werden wir die Datenformate dieser drei Teile im Detail erläutern.
1. Header
Der Header besteht in der Regel aus zwei Teilen: dem Tokentyp—üblicherweise JWT
—und dem verwendeten Signatur- oder Verschlüsselungsalgorithmus, wie z.B. HMAC SHA256
oder RSA
. Der Header wird als JSON dargestellt und anschließend in einen String konvertiert, der Base64Url verwendet. Zum Beispiel:
{
"alg": "HS256",
"typ": "JWT"
}
Nach der Codierung erhält man möglicherweise einen ähnlichen String wie diesen:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
2. Payload
Der Payload besteht aus einer Reihe von Claims, d.h. Informationen über die Entität (in der Regel ein Benutzer) und andere Daten. Der Payload kann mehrere vordefinierte Claims (auch als Registered Claims bekannt) und benutzerdefinierte Claims (Private Claims) enthalten.
Vordefinierte Claims können beinhalten:
-
iss
(Issuer): Aussteller -
exp
(Ablaufzeit): Ablaufzeit -
sub
(Subjekt): Subjekt -
aud
(Publikum): Publikum -
iat
(Ausgestellt am): Ausstellungszeit -
nbf
(Nicht vor): Gültige Zeit -
jti
(JWT ID): Eindeutige Identität des JWT
Ein Beispiel-Payload könnte wie folgt aussehen (im JSON-Format):
{
"sub": "1234567890",
"name": "John Doe",
"admin": true,
"iat": 1516239022
}
Diese Informationen werden ebenfalls mit Base64Url codiert, und man erhält einen ähnlichen String wie diesen:
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0
3. Signatur
Der Signaturbereich wird verwendet, um die beiden oben genannten codierten Strings zu signieren, um zu überprüfen, dass die Nachricht während der Übertragung nicht manipuliert wurde. Zunächst müssen Sie einen Schlüssel angeben (wenn Sie den HMAC SHA256-Algorithmus verwenden), und dann den im Header angegebenen Algorithmus verwenden, um den Header und den Payload zu signieren.
Wenn Sie beispielsweise den folgenden Header und Payload haben:
HeaderEncoded.HeaderPayload
Pseudocode zur Signierung unter Verwendung eines Schlüssels könnte sein:
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), geheimerSchlüssel)
Der resultierende signierte String könnte sein:
dBjftJeZ4CVPmTaoyL4IiArYfL4kH0jOspm6XwbcJXY
Komplettes JWT
Durch die Kombination dieser drei Teile mit einem Punkt (.) als Trennzeichen entsteht das vollständige JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.dBjftJeZ4CVPmTaoyL4IiArYfL4kH0jOspm6XwbcJXY
Der JWT-Standard ist äußerst flexibel und kann beliebige Informationen im Payload speichern (aber zum Zweck der Reduzierung der JWT-Größe und aus Sicherheitsgründen sollten keine sensiblen Informationen gespeichert werden) und gewährleistet die Integrität dieser Informationen durch die Signatur.
Installation der Go JWT-Bibliothek
Die Go-Community bietet ein Paket github.com/golang-jwt/jwt
zur Verarbeitung von JWT an. Die Installation dieser Bibliothek ist sehr einfach. Führe einfach den folgenden Befehl in deinem Projektverzeichnis aus:
go get -u github.com/golang-jwt/jwt/v5
Sobald die Installation abgeschlossen ist, kannst du sie wie folgt importieren:
import "github.com/golang-jwt/jwt/v5"
Erstellen eines einfachen Tokens
Um ein einfaches JWT-Token unter Verwendung der Go-Sprache und des HS256-Algorithmus zu erstellen, musst du folgende Schritte befolgen:
Generiere zunächst ein neues Token-Objekt unter Verwendung des HS256-Algorithmus:
token := jwt.New(jwt.SigningMethodHS256)
Verwende dann die SignedString
-Methode, um eine Zeichenfolgenrepräsentation dieses Tokens zu generieren, wobei der zu verwendende Schlüssel übergeben wird.
var mySigningKey = []byte("dein-256-Bit-Geheimnis")
strToken, err := token.SignedString(mySigningKey)
if err != nil {
log.Fatalf("Es ist ein Fehler aufgetreten: %v", err)
}
fmt.Println(strToken)
Dies wird ein einfaches Token ohne Ansprüche generieren.
Erstellen eines Tokens mit Parametern
Eine der Hauptfunktionen von JWT ist die Übermittlung von Informationen. Diese Informationen werden im Token durch Ansprüche codiert. Erstelle beispielsweise benutzerdefinierte Ansprüche:
// Struktur für benutzerdefinierte Ansprüche
type MyClaims struct {
jwt.RegisteredClaims
Username string `json:"username"`
Admin bool `json:"admin"`
}
// Erstelle ein Token mit benutzerdefinierten Ansprüchen
claims := MyClaims{
RegisteredClaims: jwt.RegisteredClaims{},
Username: "mein_benutzername",
Admin: true,
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
// Geheimer Schlüssel zum Signieren
var mySigningKey = []byte("dein-256-Bit-Geheimnis")
// Generiere das Token in Zeichenkettenformat
strToken, err := token.SignedString(mySigningKey)
if err != nil {
log.Fatalf("Es ist ein Fehler aufgetreten: %v", err)
}
fmt.Println(strToken)
Im obigen Code haben wir die Struktur MyClaims
definiert, um registrierte Ansprüche sowie einige benutzerdefinierte Informationen zu enthalten. Anschließend verwenden wir immer noch SignedString
, um die Zeichenkettenrepräsentation des Tokens zu generieren.
Parsen und Validieren des Tokens
Das Parsen und Validieren des JWT ist sehr wichtig und kann wie folgt durchgeführt werden:
// Wir verwenden dieselbe Struktur MyClaims
tokenString := "deine-JWT-Zeichenkette"
// Wir müssen eine Funktion definieren, die das jwt-Paket zum Parsen der tokenString verwenden wird
keyFunc := func(t *jwt.Token) (interface{}, error) {
// Überprüfe, dass die erwartete Signaturmethode verwendet wird
if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("Unerwartete Signaturmethode: %v", t.Header["alg"])
}
// Gib den geheimen Schlüssel für das jwt-Token im Format []byte zurück, das mit dem zuvor verwendeten Schlüssel zum Signieren übereinstimmt
return mySigningKey, nil
}
// Parsen des Tokens
claims := &MyClaims{}
parsedToken, err := jwt.ParseWithClaims(tokenString, claims, keyFunc)
if err != nil {
log.Fatalf("Parsenfehler: %v", err)
}
if !parsedToken.Valid {
log.Fatalf("Ungültiges Token")
}
// Zu diesem Zeitpunkt wurde parsedToken verifiziert, und wir können die Ansprüche lesen
fmt.Printf("Benutzer: %s, Admin: %v\n", claims.Username, claims.Admin)
Im obigen Code haben wir die Tokenzeichenfolge, die MyClaims
-Instanz und die Schlüsselfunktion keyFunc
an die Funktion jwt.ParseWithClaims
übergeben. Diese Funktion wird die Signatur überprüfen und das Token parsen, wobei die Variable claims
gefüllt wird, wenn das Token gültig ist.