如何在Go中验证JWT令牌并将其加载到结构中



这应该是一个简单的问题,但我一直很难弄清楚。

给定JWT令牌和RSA公钥/私钥对,如何将令牌转换为结构并进行验证?

type JwtData struct {
AccessToken string `json:"access_token"`
Iat         int    `json:"iat"`
Exp         int    `json:"exp"`
Sub         string `json:"sub"`
Iss         string `json:"iss"`
}
privateKeyString := "my_private_key"
publicKeyString := "my_public_key"
tokenString := "my_token_data"
decryptedData, error := whatDoIDoHere(publicKeyString, tokenString)

这似乎是";标准";JWT的Go库(有标准的吗?我觉得Go的包生态系统很混乱(https://github.com/lestrrat-go/jwx/tree/main/jwt

演示很好地涵盖了这一点,但这里有一个更完整的示例,显示了如何从编码的令牌/证书开始(请注意,演示首先生成cert/jwt;如果您对它不感兴趣,可以忽略它(。

这似乎是"标准";JWT去图书馆(有标准的吗?(

Go有一个包含许多基本功能的标准库。我不认为其他任何东西都可以被视为"标准"(这与其他语言一样;与大多数语言相比,Go标准库相当全面(。虽然有一些JWT软件包,但lestrrat-go/jwx相当全面,而且重要的是,它是积极开发的。

package main
import (
"bytes"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"time"
"github.com/lestrrat-go/jwx/jwa"
"github.com/lestrrat-go/jwx/jwk"
"github.com/lestrrat-go/jwx/jwt"
)
func main() {
token, pubKey, err := generateJWT()
if err != nil {
panic(err)
}
// JWT has been generated - lets output it (and the certificate)
fmt.Printf("JWT: %snPublic Key: %s", token, pubKey)
parsedToken, err := parseJWT(token, pubKey)
if err != nil {
panic(err)
}
// Standard claims can be accessed as follows:
fmt.Printf("Iss: %vn", parsedToken.IssuedAt())
fmt.Printf("Exp: %vn", parsedToken.Expiration())
fmt.Printf("Aud: %vn", parsedToken.Audience())
// Private Claims
fmt.Printf("Private Claims: %#vn", parsedToken.PrivateClaims())
}
// generateJWT - Generates a JWT (and signs it with a generated certificate)
func generateJWT() ([]byte, []byte, error) {
// We will start by generating a test token for which a key will be needed
privKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, nil, fmt.Errorf("failed to generate private key: %sn", err)
}
// Preparation:
// For demonstration purposes, we need to do some preparation
// Create a JWK key to sign the token (and also give a KeyID)
realKey, err := jwk.New(privKey)
if err != nil {
return nil, nil, fmt.Errorf("failed to create JWK: %sn", err)
}
realKey.Set(jwk.KeyIDKey, `mykey`)
// Create the token
token := jwt.New()
token.Set(`foo`, `bar`)
// Set a few standard keys
token.Set(jwt.IssuedAtKey, time.Now())
token.Set(jwt.ExpirationKey,  time.Now().Add(time.Hour)) // lets make it expire in an hour
token.Set(jwt.AudienceKey, "lotsOfUsers")
// Sign the token and generate a payload
signed, err := jwt.Sign(token, jwa.RS256, realKey)
if err != nil {
return nil, nil, fmt.Errorf("failed to generate signed payload: %sn", err)
}
// For completeness lets encode the public key (so the decoding matches what someone just performing the decode would need to do)
publickey := &privKey.PublicKey
publicKeyBytes, err := x509.MarshalPKIXPublicKey(publickey)
if err != nil {
return nil, nil, fmt.Errorf("error when dumping publickey: %s n", err)
}
publicKeyBlock := &pem.Block{
Type:  "PUBLIC KEY",
Bytes: publicKeyBytes,
}
publicKeyBuff := new(bytes.Buffer)
err = pem.Encode(publicKeyBuff, publicKeyBlock)
if err != nil {
return nil, nil, fmt.Errorf("error when encode public pem: %s n", err)
}
encodedPubKey := publicKeyBuff.Bytes()
return signed, encodedPubKey, nil
}
// parse the passed in JWT with the passed in certificate and return a map of claims
func parseJWT(signedJWT []byte, encodedPubKey []byte) (jwt.Token, error){
var keyset jwk.Set
privPem, _ := pem.Decode(encodedPubKey)
if privPem == nil {
return nil, fmt.Errorf("Failed to decode PEM block")
}
privPemBytes := privPem.Bytes
var parsedKeyInt interface{}
var err error
if parsedKeyInt, err = x509.ParsePKIXPublicKey(privPemBytes); err != nil {
return nil, fmt.Errorf("Failed to decode cert: %s", err)
}
parsedKey, ok := parsedKeyInt.(*rsa.PublicKey)
if !ok {
return nil, fmt.Errorf("public key not expected type")
}
// Now create a key set that users will use to verity the signed payload against
pubKey, err := jwk.New(parsedKey)
if err != nil {
return nil, fmt.Errorf("failed to create JWK: %s", err)
}
// Remember, the key must have the proper "kid"
pubKey.Set(jwk.KeyIDKey, "mykey")
// This key set contains only one key (the correct one)
keyset = jwk.NewSet()
keyset.Add(pubKey)
parsedToken, err := jwt.Parse(
signedJWT,
// Tell the parser that you want to use this keyset
jwt.WithKeySet(keyset),
// Tell the parser that you can trust this KeySet, and that
// yo want to use the sole key in it
jwt.UseDefaultKey(true),
// We want to validate the token; e.g. it should not have expired
jwt.WithValidate(true),
)
if err != nil {
return nil, fmt.Errorf("failed to parse payload: %s", err)
}
return parsedToken, nil
}

最新更新