27

我目前正在开发一个 Golang 应用程序。我从客户端收到一个 JWT 令牌,在 Go 中我需要解码该令牌并获取信息:用户、名称等。我正在检查可用于处理 JWT 的库令牌,我来到https://github.com/dgrijalva/jwt-go,但我不知道如何简单地制作我需要的东西。

我有令牌,我需要将信息解码成地图或至少一个 json。我在哪里可以找到如何操作的指南?谢谢!

4

6 回答 6

44

函数jwt.ParseWithClaims接受一个接口jwt.Claims作为第二个参数。除了基于结构的自定义声明外,该包还提供map基于结构的声明,即jwt.MapClaims. 因此,您可以简单地将令牌解码为 a 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)
}
于 2017-07-31T06:08:23.510 回答
18

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

现在是 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
}

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

于 2019-06-14T14:40:16.723 回答
5

由于问题和答案都提到了 JWT 库github.com/dgrijalva/jwt-go,请注意这个库已经很久没有维护了。

截至 2021 年 6 月,社区分叉golang-jwt/jwt由原作者 Dave Grijalva正式加持。

这也意味着库导入路径发生了变化。请注意,当前的主要版本v3不在 Go 模块上,因此您仍会v3.x.x+incompatiblego.mod.


编辑:自 2021 年 8 月版本可用v4这终于支持 Go modules 了。新版本向后兼容以前的版本,因此为了迁移只需将旧的导入路径替换为:golang-jwt/jwt

github.com/golang-jwt/jwt/v4

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


最值得注意的是,fork 修复了原始库的一个重要安全问题。在修复之前,该库没有正确处理audJWT 声明中的多个,使其实际上不符合 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
    }
于 2021-06-28T07:54:14.623 回答
4

如果您想从 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{}

注意:代码与 比较tokennil而不是err. 将errkeyFunc can't be nil

于 2020-08-29T19:31:49.907 回答
2

使用github.com/dgrijalva/jwt-gogo liabary 来实现。我们可以按照以下方式从api请求中提取JWT令牌信息。

从 using 发布请求发布 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 文件中获取公钥)

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

于 2017-07-31T07:21:17.117 回答
1

我有一种非常相似的用例,我想在解码后验证访问令牌并从中提取字段(例如:iss、、、、、、、subaud)。对于我的用例,我使用了 jwxjwt-go库。expiatjti

请在此处找到对我有用的详细代码片段。

代码片段

去.mod
module my-go-module

go 1.16

require (
    github.com/dgrijalva/jwt-go v3.2.0+incompatible
    github.com/lestrrat-go/jwx v1.0.4
)
主包
package main

import (
    "errors"
    "fmt"

    "github.com/dgrijalva/jwt-go"
    "github.com/lestrrat-go/jwx/jwa"
    "github.com/lestrrat-go/jwx/jwk"
)

func main() {
    jwksURL := "https://your-tenant.auth0.com/.well-known/jwks.json"

    keySet, _ := jwk.Fetch(jwksURL)
    var accessToken = "eyJhbGciOiJSUzI1NiIsImtpZCI6Ind5TXdLNEE2Q0w5UXcxMXVvZlZleVExMTlYeVgteHlreW1ra1h5Z1o1T00ifQ.eyJzdWIiOiIwMHUxOGVlaHUzNDlhUzJ5WDFkOCIsIm5hbWUiOiJva3RhcHJveHkgb2t0YXByb3h5IiwidmVyIjoxLCJpc3MiOiJodHRwczovL2NvbXBhbnl4Lm9rdGEuY29tIiwiYXVkIjoidlpWNkNwOHJuNWx4ck45YVo2ODgiLCJpYXQiOjE0ODEzODg0NTMsImV4cCI6MTQ4MTM5MjA1MywianRpIjoiSUQuWm9QdVdIR3IxNkR6a3RUbEdXMFI4b1lRaUhnVWg0aUotTHo3Z3BGcGItUSIsImFtciI6WyJwd2QiXSwiaWRwIjoiMDBveTc0YzBnd0hOWE1SSkJGUkkiLCJub25jZSI6Im4tMFM2X1d6QTJNaiIsInByZWZlcnJlZF91c2VybmFtZSI6Im9rdGFwcm94eUBva3RhLmNvbSIsImF1dGhfdGltZSI6MTQ4MTM4ODQ0MywiYXRfaGFzaCI6Im1YWlQtZjJJczhOQklIcV9CeE1ISFEifQ.OtVyCK0sE6Cuclg9VMD2AwLhqEyq2nv3a1bfxlzeS-bdu9KtYxcPSxJ6vxMcSSbMIIq9eEz9JFMU80zqgDPHBCjlOsC5SIPz7mm1Z3gCwq4zsFJ-2NIzYxA3p161ZRsPv_3bUyg9B_DPFyBoihgwWm6yrvrb4rmHXrDkjxpxCLPp3OeIpc_kb2t8r5HEQ5UBZPrsiScvuoVW13YwWpze59qBl_84n9xdmQ5pS7DklzkAVgqJT_NWBlb5uo6eW26HtJwHzss7xOIdQtcOtC1Gj3O82a55VJSQnsEEBeqG1ESb5Haq_hJgxYQnBssKydPCIxdZiye-0Ll9L8wWwpzwig"
    token, err := verify(accessToken, keySet)
    if err != nil {
        fmt.Printf("Gor an error while verifiying access token: %v\n", err)
    }

    // Check if the token is valid.
    if !token.Valid {
        fmt.Println("The token is not valid.")
    }

    // Extract key value from the token and print them on console
    claims := token.Claims.(jwt.MapClaims)
    for key, value := range claims {
        fmt.Printf("%s\t%v\n", key, value)
    }
}

func verify(tokenString string, keySet *jwk.Set) (*jwt.Token, error) {
    tkn, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
        if token.Method.Alg() != jwa.RS256.String() { 
            return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
        }
        kid, ok := token.Header["kid"].(string)
        if !ok {
            return nil, errors.New("kid header not found")
        }
        keys := keySet.LookupKeyID(kid)
        if len(keys) == 0 {
            return nil, fmt.Errorf("key %v not found", kid)
        }
        var raw interface{}
        return raw, keys[0].Raw(&raw)
    })
    return tkn, err
}
于 2021-09-15T17:48:31.090 回答