在循环中创建 JWT 令牌时在 jwt-go 中获取相同的令牌

When create JWT token inside loop getting same token in jwt-go

我正在使用 jwt-go 库创建 jwt 令牌。后来写了一个脚本来加载测试。我注意到当我发送许多获得相同令牌的并发请求时。为了检查更多相关信息,我在 for 循环中创建了令牌,结果是相同的。

我用的库是https://github.com/dgrijalva/jwt-go,go版本是1.12.9.

expirationTime := time.Now().Add(time.Duration(60) * time.Minute)

    for i := 1; i < 5; i++ {
        claims := &jwt.StandardClaims{
            ExpiresAt: expirationTime.Unix(),
            Issuer:"telescope",
        }
        _token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
        var jwtKey = []byte("secret_key")
        auth_token, _ := _token.SignedString(jwtKey)
        fmt.Println(auth_token)
    }

我更改了您的代码:

  • 在循环中移动了 expirationTime 的计算
  • 在循环的每一步添加 1 秒延迟

    for i := 1; i < 5; i++ {
    
        expirationTime := time.Now().Add(time.Duration(60) * time.Minute)
    
        claims := &jwt.StandardClaims{
            ExpiresAt: expirationTime.Unix(),
            Issuer:    "telescope",
        }
        _token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
        var jwtKey = []byte("secret_key")
        auth_token, _ := _token.SignedString(jwtKey)
        fmt.Println(auth_token)
    
        time.Sleep(time.Duration(1) * time.Second)
    }
    

在这种情况下,我们得到不同的标记:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjcyNDcwNDgsImlzcyI6InRlbGVzY29wZSJ9.G7wV-zsCYjysLEdgYAq_92JGDPsgqqOz9lZxdh5gcX8
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjcyNDcwNDksImlzcyI6InRlbGVzY29wZSJ9.yPNV20EN3XJbGiHhe-wGTdiluJyVHXj3nIqEsfwDZ0Q
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjcyNDcwNTAsImlzcyI6InRlbGVzY29wZSJ9.W3xFXEiVwh8xK47dZinpXFpKuvUl1LFUAiaLZZzZ2L0
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjcyNDcwNTEsImlzcyI6InRlbGVzY29wZSJ9.wYUbzdXm_VQGdFH9RynAVVouW9h6KI1tHRFJ0Y322i4

抱歉,我不是 JWT 专家,我希望有人从 RFC 的角度向我们解释这种行为。

I want to get different tokens. eg : same person login in to system using different browser. so I want to keep many tokens.

是同一个用户,我们可以给他取同样的token。如果我们想再给它一个,我们需要撤销之前的一个,否则客户端必须刷新它。

一个 JWT 包含三个部分:一个 mostly-fixed header,一组 claims,以及一个签名RFC 7519 有实际细节。如果 header 是固定的并且两个令牌之间的声明相同,那么签名也将相同,并且您可以轻松获得重复的令牌。两个时间戳声明“iat”和“exp”仅在第二个粒度上,因此如果您在同一秒内使用您的代码发出多个令牌,您将获得相同的结果(即使您将 expirationTime 计算移到循环)。

jwt-go 库导出 StandardClaims listed in RFC 7519 §4.1 as a structure, which is what you're using in your code. Digging through the library code, there's nothing especially subtle here: StandardClaims uses ordinary "encoding/json" annotations, and then when a token is written out, the claims are JSON encoded and then base64-encoded。所以给定一个固定的输入,你会得到一个固定的输出。

如果您希望每个令牌都以某种方式“不同”,the standard "jti" claim 是提供唯一 ID 的地方。这不是 StandardClaims 的一部分,因此您需要创建包含它的自定义声明类型。

type UniqueClaims struct {
    jwt.StandardClaims
    TokenId string `json:"jti,omitempty"`
}

然后当你创建声明结构时,你需要自己生成一个唯一的TokenId

import (
    "crypto/rand"
    "encoding/base64"
)

bits := make([]byte, 12)
_, err := rand.Read(bits)
if err != nil {
    panic(err)
}
claims := UniqueClaims{
    StandardClaims: jwt.StandardClaims{...},
    TokenId: base64.StdEncoding.EncodeToString(bits),
}

https://play.golang.org/p/zDnkamwsCi- has a complete example; every time you run it you will get a different token, even if you run it multiple times in the same second. You can base64 decode the middle part of the token by hand to see the claims, or use a tool like the https://jwt.io/ 调试器对其进行解码。