0

我正在使用 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)
    }
4

2 回答 2

3

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

jwt-go 库将RFC 7519 §4.1中列出的StandardClaims导出为结构,这是您在代码中使用的结构。挖掘库代码,这里没有什么特别微妙的地方:StandardClaims使用普通注释,然后在写出令牌时,声明是 JSON 编码,然后是 base64 编码。所以给定一个固定的输入,你会得到一个固定的输出。"encoding/json"

如果您希望每个令牌以某种方式“不同”,则标准的“jti”声明是提供唯一 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-有完整的例子;每次运行它都会得到一个不同的令牌,即使你在同一秒内多次运行它也是如此。您可以手动对令牌的中间部分进行 base64 解码以查看声明,或使用https://jwt.io/调试器之类的工具对其进行解码。

于 2019-08-31T10:34:38.407 回答
0

我改变了你的代码:

  • 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 的角度向我们解释这种行为。

我想获得不同的令牌。例如:同一个人使用不同的浏览器登录系统。所以我想保留很多代币。

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

于 2019-08-31T09:28:39.840 回答