英文:
How to decode a JWT token in Go?
问题
我目前正在开发一个Go应用程序。我从客户端接收到一个JWT令牌,我需要解码该令牌并获取相关信息:用户、姓名等。
我正在查看可用于处理JWT令牌的库,我找到了dgrijalva/jwt-go
,但我不知道如何以简单的方式实现我的目标。
我有令牌,我需要将信息解码为一个映射或至少一个JSON。我该如何做到这一点?
英文:
I am currently working on a Go application. I receive a JWT token from the client side and I need to decode that token and obtain the relevant information: user, name, etc.
I was checking the libraries that are available to handle JWT tokens and I came down to dgrijalva/jwt-go
, but I don't see how to accomplish my goal in a simple way.
I have the token and I need to decode the info into a map or at least a json. How can I do it?
答案1
得分: 53
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
})
// ... 错误处理
// 使用解码后的声明进行操作
for key, val := range claims {
fmt.Printf("Key: %v, value: %v\n", key, val)
}
英文:
Function 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
.
So, you can simply decode the token into a MapClaims
, e.g.
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)
}
答案2
得分: 24
免责声明:我与该库无关。我只是一个用户,发现它很有用,想分享一下。
现在是2019年。我想建议一个备选库,它在使用JWS和/或JWE处理JWT方面做得非常好。
以下是如何使用该库的几个示例:
import (
"gopkg.in/square/go-jose.v2/jwt"
"gopkg.in/square/go-jose.v2"
)
...
var claims map[string]interface{} // 用于存储解析后的令牌的通用映射
// 解码JWT令牌,但不验证签名
token, _ := jwt.ParseSigned(tokenString)
_ = token.UnsafeClaimsWithoutVerification(&claims)
// 解码JWT令牌并使用JSON Web Keyset验证签名
token, _ := jwt.ParseSigned(tokenString)
jwks := &jose.JSONWebKeySet { // 通常可以从授权服务器公开的端点获取此信息
Keys: []jose.JSONWebKey { // 仅为示例
{
Key: publicKey,
Algorithm: jose.RS256, // 应与JWT令牌头中的算法相同
KeyID: "kid", // 应与JWT令牌头中的KeyID相同
},
},
}
_ = jwt.Claims(jwks, &claims)
请注意,claims
可以是一个包含默认JWT字段和令牌内部的自定义字段的结构体。
例如:
import (
"github.com/mitchellh/mapstructure"
"gopkg.in/square/go-jose.v2/jwt"
)
...
type CustomClaims struct {
*jwt.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
}
我还构建了一个命令行工具,它使用该库执行各种编码/解码操作。这也可能是使用该库的一个有用参考。
英文:
Disclaimer: I am not affiliated to the library. I'm just a user, find it useful and would like to share.
It's 2019. I would like to suggest an alternate library that does pretty good job on JWT using JWS and/or JWE.
Here is the few examples on how to use the library:
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)
Noted that, claims
can be a struct that contains default JWT fields and also customized fields that are inside the token
E.g.:
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
}
I also built a command line tool that uses the library to perform various encoding/decoding activities. It might be a useful reference on the usage of the library too.
答案3
得分: 17
由于问题和答案都提到了JWT库github.com/dgrijalva/jwt-go
,请注意这个库已经很久没有维护了。
截至2021年6月,有一个社区分支golang-jwt/jwt,由原作者Dave Grijalva正式认可。
这也意味着库的导入路径已经改变。请注意,<strike>当前</strike>主要版本v3
不支持Go模块,因此在你的go.mod
中仍然会看到v3.x.x+incompatible
。
<hr>
编辑:自2021年8月起,golang-jwt/jwt
的v4
版本可用。这个版本终于支持Go模块。新版本与之前的版本向后兼容,因此只需将旧的导入路径替换为:
github.com/golang-jwt/jwt/v4
然后根据需要更新你的模块——详见迁移指南。
<hr>
这个分支主要修复了原始库的一个重要安全问题。在修复之前,该库没有正确处理JWT声明中的多个aud
,实际上不符合JWT规范。
除此之外,主要的API仍然相同。例如,要使用HMAC验证解析JWT:
tokenString := /* 原始JWT字符串 */
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(/* 你的JWT密钥*/), nil
})
if err != nil {
// 处理错误
}
// 验证必要的声明
if !token.Valid {
// 处理无效的令牌
}
要解析带有自定义声明的JWT,你可以定义自己的结构类型并将jwt.StandardClaims
嵌入其中:
type MyClaims struct {
jwt.StandardClaims
MyField string `json:"my_field"`
}
tokenString := /* 原始JWT字符串 */
// 将自定义声明传递给解析函数
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(/* 你的JWT密钥*/), nil
})
// 将`Claims`类型断言为适当类型的变量
myClaims := token.Claims.(*MyClaims)
<hr>
这个库的一个有效替代品是lestrrat-go/jwx
。API略有不同,但同样易于使用:
tokenString := /* 原始JWT字符串 */
// 解析并验证签名
tok, err := jwt.Parse(tokenString, jwt.WithVerify(jwa.HS256, []byte(/* 你的JWT密钥 */)))
if err != nil {
// 处理错误
}
// 验证必要的声明
if err := jwt.Validate(tok); err != nil {
// 处理错误
}
英文:
Since both the question and answers mention the JWT library github.com/dgrijalva/jwt-go
, please note that this library has been unmaintained for a long time now.
As of June 2021 there is a community fork golang-jwt/jwt, officially blessed by Dave Grijalva, the original author.
This also means that the library import path has changed. Note that the <strike>current</strike> major version v3
is not on Go modules, therefore you will still see v3.x.x+incompatible
in your go.mod
.
<hr>
Edit: since August 2021 version v4
of golang-jwt/jwt
is available. This finally supports Go modules. The new version is backward-compatible with previous versions, so in order to migrate simply replace the old import path with:
github.com/golang-jwt/jwt/v4
then update your modules as needed — see also the migration guide for details.
<hr>
The fork most notably fixes an important security issue with the original library. Before the fix, the library didn't properly handle multiple aud
in the JWT claims, making it actually not compliant with the JWT spec.
Apart from that, the main API is still the same. For example to parse a JWT with HMAC verification:
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
}
To parse a JWT with custom claims, you can define your own struct type and embed jwt.StandardClaims
into it:
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)
<hr>
A valid alternative to this library is lestrrat-go/jwx
. The API is slightly different, but also very easy to use:
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
}
答案4
得分: 8
如果你想从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实际上是一个map[string]interface{}
注意:代码将token
与nil
进行比较,而不是err
。err
将是keyFunc can't be nil
。
英文:
If you want to get claims from jwt token without validation
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{}
Note: code compares token
with nil
, not the err
. The err
will be keyFunc can't be nil
.
答案5
得分: 2
使用github.com/dgrijalva/jwt-go
Go库进行实现。我们可以根据以下方式从API请求中提取JWT令牌信息。
当使用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文件获取公钥)
如果有任何问题,请告诉我。我可以提供帮助。
英文:
Use github.com/dgrijalva/jwt-go
go liabary for the implementation. we can extract JWT token information from the api request according to the following way.
When post the JWT token from the using post request. you must extract JWT information in routing section.
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 : The key for verification(get public key from public.key file in your system)
Any Issue happen.Please let me know. I can give you help.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论