如何在 Go 中解码 JWT 令牌?
How to decode a JWT token in Go?
我目前正在开发 Go 应用程序。我从客户端收到一个 JWT 令牌,我需要解码该令牌并获取相关信息:用户、名称等。
我正在检查可用于处理 JWT 令牌的库,结果发现 dgrijalva/jwt-go
,但我看不出如何以简单的方式实现我的目标。
我有令牌,我需要将信息解码为地图或至少 json。我该怎么做?
函数jwt.ParseWithClaims
accept an interface of jwt.Claims
as the second argument. Besides struct-based custom claims, the package also provides map
-based claims, i.e. 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)
}
使用github.com/dgrijalva/jwt-go
go liabary 实现。我们可以按照下面的方式从api请求中提取JWT token信息
当 post 来自使用 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 文件中获取 public 密钥)
任何问题 happen.Please 让我知道。我可以帮你。
免责声明:我不隶属于图书馆。我只是一个用户,觉得有用,愿意分享。
现在是 2019 年。我想推荐一个 alternate library 使用 JWS and/or 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
}
我还构建了 a command line tool that uses the library 来执行各种 encoding/decoding 活动。它也可能是有关库用法的有用参考。
如果您想从 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
。
由于问题和答案都提到了 JWT 库 github.com/dgrijalva/jwt-go
,请注意这个库 已经很长时间没有维护了。
截至 2021 年 6 月,原作者 Dave Grijalva 发布了一个社区分支 golang-jwt/jwt, officially blessed。
这也意味着库导入路径发生了变化。请注意,当前 主要版本 v3
不在 Go 模块中,因此您仍会在 go.mod
.
中看到 v3.x.x+incompatible
编辑: 自 2021 年 8 月起 golang-jwt/jwt
的 v4
版本可用。 终于支持 Go 模块。新版本与以前的版本向后兼容,所以为了迁移只需将旧的导入路径替换为:
github.com/golang-jwt/jwt/v4
然后根据需要更新您的模块 — 另请参阅 migration guide 了解详细信息。
分叉最显着地修复了 an important security issue 与原始库。在修复之前,库没有正确处理 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
}
我目前正在开发 Go 应用程序。我从客户端收到一个 JWT 令牌,我需要解码该令牌并获取相关信息:用户、名称等。
我正在检查可用于处理 JWT 令牌的库,结果发现 dgrijalva/jwt-go
,但我看不出如何以简单的方式实现我的目标。
我有令牌,我需要将信息解码为地图或至少 json。我该怎么做?
函数jwt.ParseWithClaims
accept an interface of jwt.Claims
as the second argument. Besides struct-based custom claims, the package also provides map
-based claims, i.e. 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)
}
使用github.com/dgrijalva/jwt-go
go liabary 实现。我们可以按照下面的方式从api请求中提取JWT token信息
当 post 来自使用 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 文件中获取 public 密钥)
任何问题 happen.Please 让我知道。我可以帮你。
免责声明:我不隶属于图书馆。我只是一个用户,觉得有用,愿意分享。
现在是 2019 年。我想推荐一个 alternate library 使用 JWS and/or 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
}
我还构建了 a command line tool that uses the library 来执行各种 encoding/decoding 活动。它也可能是有关库用法的有用参考。
如果您想从 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
。
由于问题和答案都提到了 JWT 库 github.com/dgrijalva/jwt-go
,请注意这个库 已经很长时间没有维护了。
截至 2021 年 6 月,原作者 Dave Grijalva 发布了一个社区分支 golang-jwt/jwt, officially blessed。
这也意味着库导入路径发生了变化。请注意,当前 主要版本 v3
不在 Go 模块中,因此您仍会在 go.mod
.
v3.x.x+incompatible
编辑: 自 2021 年 8 月起 golang-jwt/jwt
的 v4
版本可用。 终于支持 Go 模块。新版本与以前的版本向后兼容,所以为了迁移只需将旧的导入路径替换为:
github.com/golang-jwt/jwt/v4
然后根据需要更新您的模块 — 另请参阅 migration guide 了解详细信息。
分叉最显着地修复了 an important security issue 与原始库。在修复之前,库没有正确处理 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
}