Golang中无过期时间JWT验证提示过期问题求助
问题分析与解决方案
核心问题原因
- Claims类型不匹配:生成令牌时使用
jwt.MapClaims,但验证时用自定义的CustomClaims(仅包含StandardClaims),导致自定义字段丢失,且无exp字段时StandardClaims.ExpiresAt默认值为0。 - 错误的过期判断逻辑:你的中间件中
claims.ExpiresAt < time.Now().Unix()会永远为真(因为0肯定小于当前时间戳),这就是为什么明明令牌有效却提示过期的根本原因。 - 冗余的手动验证:
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
相关产品推荐
相关产品推荐

