You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Golang中无过期时间JWT验证提示过期问题求助

问题分析与解决方案

核心问题原因

  1. Claims类型不匹配:生成令牌时使用jwt.MapClaims,但验证时用自定义的CustomClaims(仅包含StandardClaims),导致自定义字段丢失,且无exp字段时StandardClaims.ExpiresAt默认值为0。
  2. 错误的过期判断逻辑:你的中间件中claims.ExpiresAt < time.Now().Unix()会永远为真(因为0肯定小于当前时间戳),这就是为什么明明令牌有效却提示过期的根本原因。
  3. 冗余的手动验证:jwt.ParseWithClaims本身会自动验证令牌签名和过期时间(如果存在exp字段),无需额外重复判断,但需要兼容无exp的场景。

修正方案

方案1:使用强类型CustomClaims(推荐)

首先修正令牌生成代码(无需手动设置exp:nil,不添加该字段即为永不过期):

func GenerateToken(user models.User) (string, error) {
    claims := jwt.MapClaims{
        "user":     user.Email,
        "nombre":   user.Nombre,
        "apellido": user.Apellido,
        "edad":     fmt.Sprint(user.Edad),
        "genero":   user.Genero,
        "rol":      user.Rol,
        // 不设置exp字段,令牌永久有效
    }

    tokenBuilder := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    tokenString, err := tokenBuilder.SignedString([]byte(os.Getenv("SECRET")))

    return tokenString, err
}

然后修改中间件的CustomClaims,添加所有自定义字段,并调整过期判断逻辑:

type CustomClaims struct {
    User     string `json:"user"`
    Nombre   string `json:"nombre"`
    Apellido string `json:"apellido"`
    Edad     string `json:"edad"`
    Genero   string `json:"genero"`
    Rol      string `json:"rol"`
    jwt.StandardClaims
}

func JWTMiddleware() fiber.Handler {
    return func(c *fiber.Ctx) error {
        authHeader := c.Get("Authorization")
        if authHeader == "" {
            return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
                "message": "No Auth token",
            })
        }

        tokenString := authHeader[7:] // 移除"Bearer "前缀

        token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
            // 验证签名算法是否合法
            if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
                return nil, fmt.Errorf("invalid signing method")
            }
            return []byte(os.Getenv("SECRET")), nil
        })

        if err != nil {
            return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
                "message": "Invalid token",
            })
        }

        claims, ok := token.Claims.(*CustomClaims)
        if !ok || !token.Valid {
            return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
                "message": "Invalid or expired token",
            })
        }

        // 仅当ExpiresAt不为0时,才检查过期(0表示无过期时间)
        if claims.ExpiresAt != 0 && claims.ExpiresAt < time.Now().Unix() {
            return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
                "message": "Expired token",
            })
        }

        // 将Claims存入上下文,供后续路由使用
        c.Locals("userClaims", claims)

        return c.Next()
    }
}

方案2:使用MapClaims兼容任意字段

如果不需要强类型Claims,可直接用MapClaims验证,更灵活:

func JWTMiddleware() fiber.Handler {
    return func(c *fiber.Ctx) error {
        authHeader := c.Get("Authorization")
        if authHeader == "" {
            return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
                "message": "No Auth token",
            })
        }

        tokenString := authHeader[7:]

        token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
            if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
                return nil, fmt.Errorf("invalid signing method")
            }
            return []byte(os.Getenv("SECRET")), nil
        })

        if err != nil {
            return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
                "message": "Invalid token",
            })
        }

        claims, ok := token.Claims.(jwt.MapClaims)
        if !ok || !token.Valid {
            return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
                "message": "Invalid or expired token",
            })
        }

        // 仅当exp字段存在时,才验证过期
        if expVal, ok := claims["exp"].(float64); ok {
            if int64(expVal) < time.Now().Unix() {
                return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
                    "message": "Expired token",
                })
            }
        }

        c.Locals("userClaims", claims)
        return c.Next()
    }
}

关键注意点

  • 生成永不过期令牌时,直接不添加exp字段即可,无需手动设置为nil或0。
  • 验证时必须确保Claims类型与生成时匹配,或使用MapClaims兼容动态字段。
  • 无exp字段的令牌,StandardClaims.ExpiresAt默认值为0,必须跳过过期判断,否则会误判。

内容的提问来源于stack exchange,提问作者Hey L1nk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 14:25:31