如何在 Go 中解码 JWT 令牌?

问题描述 投票:0回答:6

我目前正在开发 Go 应用程序。我从客户端收到一个 JWT 令牌,我需要解码该令牌并获取相关信息:用户、名称等。

我正在检查可用于处理 JWT 令牌的库,最后发现

dgrijalva/jwt-go
,但我不知道如何以简单的方式实现我的目标。

我有令牌,我需要将信息解码为映射或至少为 json。我该怎么办?

go jwt jwt-go
6个回答
54
投票

函数

jwt.ParseWithClaims
接受
jwt.Claims
的接口作为第二个参数。除了基于结构的自定义声明之外,该包还提供基于
map
的声明,即
jwt.MapClaims
。 因此,您可以简单地将令牌解码为
MapClaims
,例如

tokenString := "<YOUR TOKEN STRING>"    
claims := jwt.MapClaims{}
token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
    return []byte("<YOUR VERIFICATION KEY>"), nil
})
// ... error handling

// do something with decoded claims
for key, val := range claims {
    fmt.Printf("Key: %v, value: %v\n", key, val)
}

24
投票

免责声明:我不隶属于图书馆。我只是一个用户,觉得有用并愿意分享。

现在是 2019 年了。我想推荐一个 替代库,它使用 JWS 和/或 JWE 在 JWT 上做得非常好。

以下是有关如何使用该库的几个示例:

import (
    "gopkg.in/square/go-jose.v2/jwt"
    "gopkg.in/square/go-jose.v2"
)
...

var claims map[string]interface{} // generic map to store parsed token

// decode JWT token without verifying the signature
token, _ := jwt.ParseSigned(tokenString)
_ = token.UnsafeClaimsWithoutVerification(&claims)

// decode JWT token and verify signature using JSON Web Keyset
token, _ := jwt.ParseSigned(tokenString)
jwks := &jose.JSONWebKeySet { // normally you can obtain this from an endpoint exposed by authorization server
            Keys: []jose.JSONWebKey { // just an example
                {
                    Key: publicKey, 
                    Algorithm: jose.RS256, // should be the same as in the JWT token header
                    KeyID: "kid", // should be the same as in the JWT token header
                },
            },
        }
_ = jwt.Claims(jwks, &claims)

请注意,

claims
可以是一个包含默认 JWT 字段以及令牌内的自定义字段的结构 例如:

import (
    "github.com/mitchellh/mapstructure"
    "gopkg.in/square/go-jose.v2/jwt"
)
...
type CustomClaims struct {
    *jwt.Claims
    // additional claims apart from standard claims
    extra map[string]interface{}
}

func (cc *CustomClaims) UnmarshalJSON(b []byte) error {
    var rawClaims map[string]interface{}
    if err := json.Unmarshal(b, &rawClaims); err != nil {
        return nil
    }
    var claims jwt.Claims
    var decoderResult mapstructure.Metadata
    decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
        Result:   &claims,
        Metadata: &decoderResult,
        TagName:  "json",
    })
    if err != nil {
        return err
    }
    if err := decoder.Decode(rawClaims); err != nil {
        return err
    }
    cc.Claims = &claims
    cc.extra = make(map[string]interface{})
    for _, k := range decoderResult.Unused {
        cc.extra[k] = rawClaims[k]
    }
    return nil
}

我还构建了一个命令行工具,它使用来执行各种编码/解码活动。它也可能是有关库的使用的有用参考。


21
投票

由于问题和答案都提到了 JWT 库

github.com/dgrijalva/jwt-go
,请注意,这个库已经很长时间没有维护了。

截至 2021 年 6 月,有一个社区分叉 golang-jwt/jwt,由原作者 Dave Grijalva 正式祝福

这也意味着库导入路径发生了变化。请注意,当前主要版本

v3
不在Go模块上,因此您仍然会在
v3.x.x+incompatible
中看到
go.mod


编辑: 自 2021 年 8 月起,

v4
golang-jwt/jwt
版本可用。 这终于支持Go模块了。新版本与以前的版本向后兼容,因此为了迁移,只需将旧的导入路径替换为:

github.com/golang-jwt/jwt/v4

然后根据需要更新您的模块 - 另请参阅迁移指南了解详细信息。


该分叉最显着地修复了原始库的一个重要的安全问题。在修复之前,该库没有正确处理 JWT 声明中的多个 aud

,使其实际上不符合 JWT 规范。

除此之外,主要 API 仍然相同。例如,解析带有 HMAC 验证的 JWT:

tokenString := /* raw JWT string*/ token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { return nil, errors.New("unexpected signing method") } return []byte(/* your JWT secret*/), nil }) if err != nil { // handle err } // validate the essential claims if !token.Valid { // handle invalid tokebn }
要使用自定义声明解析 JWT,您可以定义自己的结构类型并将 

jwt.StandardClaims

 嵌入其中:

type MyClaims struct { jwt.StandardClaims MyField string `json:"my_field"` } tokenString := /* raw JWT string*/ // pass your custom claims to the parser function token, err := jwt.ParseWithClaims(tokenString, &MyClaims{}, func(token *jwt.Token) (interface{}, error) { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { return nil, errors.New("unexpected signing method") } return []byte(/* your JWT secret*/), nil }) // type-assert `Claims` into a variable of the appropriate type myClaims := token.Claims.(*MyClaims)


此库的有效替代方案是

lestrrat-go/jwx

。 API 略有不同,但也非常易于使用:

tokenString := /* raw JWT string*/ // parse and verify signature tok, err := jwt.Parse(tokenString, jwt.WithVerify(jwa.HS256, []byte(/* your JWT secret */))) if err != nil { // handle err } // validate the essential claims if err := jwt.Validate(tok); err != nil { // handle err }
    

10
投票
如果您想从 jwt 令牌获取声明

无需验证

import "github.com/dgrijalva/jwt-go" ... token, err := jwt.Parse(tokenStr, nil) if token == nil { return nil, err } claims, _ := token.Claims.(jwt.MapClaims) // claims are actually a map[string]interface{}
注意:代码将 

token

nil
 进行比较,而不是 
err
err
 将是 
keyFunc can't be nil


2
投票
使用

github.com/dgrijalva/jwt-go

去实验室进行实施。我们可以按照以下方式从api请求中提取JWT token信息。

当使用 post 请求发布 JWT 令牌时。您必须在路由部分提取 JWT 信息。

func RequireTokenAuthentication(inner http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { token, err := jwt.ParseFromRequest( r, func(token *jwt.Token) (interface{}, error) { return VERIFICATION.PublicKey, nil }) if err != nil || !token.Valid) { log.Debug("Authentication failed " + err.Error()) w.WriteHeader(http.StatusForbidden) return } else { r.Header.Set("username", token.Claims["username"].(string)) r.Header.Set("userid", strconv.FormatFloat((token.Claims["userid"]).(float64), 'f', 0, 64)) } inner.ServeHTTP(w, r) }) }

VERIFICATION.PublicKey :验证密钥(从系统中的public.key文件获取公钥)

发生任何问题。请告诉我。我可以给你帮助。


0
投票
另一种可能更好的解析声明的方法

无需令牌签名验证使用github.com/golang-jwt

同样,这仅适用于您知道签名有效的情况(因为它已经或将在堆栈中的其他位置进行检查)并且您想要从中提取值。就我而言,它是带有 JWT Authorizer 的 API 网关之后的 AWS lambda,所以我只想从令牌中获取一些数据。

import "github.com/golang-jwt/jwt/v5" claims := jwt.MapClaims{} _, _, err := jwt.NewParser().ParseUnverified(tokenString, claims) if err != nil { return "", err } for key, val := range claims { fmt.Printf("Key: %v, value: %v\n", key, val) }
我认为这个更好,因为它不会因为 nil keyFunc 而忽略错误。

© www.soinside.com 2019 - 2024. All rights reserved.