Go Fiber 的 fiber.JWT 中间件默认只存原始 *jwt.Token 到 c.Locals("user"),需配置 ParseTokenFunc 才能获取 map[string]interface{} 或自定义 claims;JWT 中数字类字段应统一用 float64 或字符串(如 user_id 转 string)避免精度丢失;Cookie 存 Token 必须设 HttpOnly、Secure、SameSite 且 Expires ≤ exp,登出需服务端黑名单配合。

Go Fiber 本身不内置认证逻辑,直接用 fiber.JWT 或手写中间件都行,但多数人踩坑是因为混淆了「会话状态维护」和「Token 验证」这两个不同层级的事。
为什么 fiber.JWT 中间件验证后拿不到用户 ID
常见现象:调用 c.Locals("user") 返回 nil,或类型断言失败。根本原因是 fiber.JWT 默认只解析 Token 并存入 c.Locals("user"),但存的是原始 *jwt.Token 对象,不是你期望的 map[string]interface{} 或自定义结构体。
- 必须显式配置
Config.ParseTokenFunc,否则c.Locals("user")里只有未解析的 Token 实例 - 如果用
github.com/golang-jwt/jwt/v5,注意ParseTokenFunc的签名要匹配:它接收*jwt.Token,返回interface{}和error - 别在中间件外直接读
c.Locals("user")—— 确保路由已挂载该中间件,且请求带了合法Authorization: Bearer xxx
config := jwt.Config{
SigningKey: jwt.SigningKey{Key: []byte("secret")},
ParseTokenFunc: func(token *jwt.Token) (interface{}, error) {
claims, ok := token.Claims.(jwt.MapClaims)
if !ok {
return nil, fmt.Errorf("invalid claims type")
}
return claims, nil // 这样 c.Locals("user") 才是 map[string]interface{}
},
}
app.Use(jwt.New(config))
登录接口返回 JWT 时,jwt.NewWithClaims 的 key 类型必须匹配
生成 Token 时用错 key 类型,会导致后续 ParseTokenFunc 解析失败或字段丢失。比如你在 MapClaims 里塞了 int64 用户 ID,但前端传参或数据库查出来是 int,JSON 序列化后可能被截断或转成 float64。
- 统一用
float64存数字类 claim(JWT 标准要求数值为 JSON number,Go 的json.Marshal会把int转成float64) - 用户 ID 建议转成字符串存,避免精度丢失:
"user_id": strconv.FormatUint(userID, 10) - 别用结构体指针初始化
MapClaims,直接构造jwt.MapClaims字面量更可控
claims := jwt.MapClaims{
"user_id": strconv.FormatUint(user.ID, 10),
"exp": time.Now().Add(time.Hour * 24).Unix(),
"iat": time.Now().Unix(),
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
需要记住登录态?别硬套 fiber.Cookie 存 JWT
把 JWT 写进 Cookie 看似方便,但默认设置下极易出安全问题:没设 HttpOnly、Secure、SameSite,或过期时间与 Token 内置 exp 不一致,导致登出失效、CSRF 漏洞、HTTPS 下 Cookie 被拒。
- Cookie 的
Expires必须 ≤ Token 的exp,否则客户端可能拿着过期 Token 继续发请求 - 开发环境可关
Secure,但上线必须开;SameSite推荐Lax(兼顾安全性与跨站登录) - 登出时仅删 Cookie 不够——JWT 是无状态的,真正登出得靠服务端黑名单(Redis)或缩短 Token 有效期
c.Cookie(&fiber.Cookie{
Name: "auth_token",
Value: tokenString,
Expires: time.Now().Add(time.Hour * 24),
HTTPOnly: true,
Secure: true, // 生产环境必须
SameSite: fiber.CookieSameSiteLaxMode,
})
最麻烦的其实是 Token 刷新和黑名单协同——Fiber 不管这些,你得自己用 Redis 存 jti + exp,再在 ParseTokenFunc 里查。这点很容易被跳过,结果上线后发现无法主动让 Token 失效。


















