因为Fiber官方无fiber.JWT中间件,需用社区库github.com/gofiber/jwt/v3;常见失败原因包括未传非空SigningKey、TokenLookup未适配cookie/query、密钥长度不足32字节导致签名静默失败。

为什么直接用 fiber.JWT 会失败?
因为 Fiber 官方并没有 fiber.JWT 这个中间件 —— 这是很多人卡住的第一步。社区常用的是 github.com/gofiber/jwt/v3(v3 版本)或更轻量的自定义方案。它不处理登录逻辑,只校验已有 token;你必须自己实现账号密码验证、签发 token、返回响应这一整套流程。
常见错误现象:jwt.New() panic: interface conversion: interface {} is nil, not string,通常是因为没传 SigningKey,或 key 是空字符串。
- 必须显式提供非空的
SigningKey(建议用os.Getenv("JWT_SECRET")管理) -
TokenLookup默认只从Authorization: Bearer xxx提取,若前端放在 cookie 或 query,需手动配置 - v3 版本默认使用 HS256,密钥长度至少 32 字节,否则签名会静默失败
如何手写一个安全的登录 handler?
不要依赖第三方“认证包”,直接用 golang.org/x/crypto/bcrypt 校验密码 + github.com/gofiber/jwt/v3 签发 token,可控且无隐藏逻辑。
关键点在于:密码比对成功后,用用户 ID(或其他不可变标识)生成 token,并避免把敏感字段(如密码哈希、邮箱)塞进 payload。
func login(c *fiber.Ctx) error {
var req struct {
Username string `json:"username"`
Password string `json:"password"`
}
if err := c.BodyParser(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid json"})
}
<p>// 查库获取 user(假设 user.ID 是 uint)
user, err := db.FindUserByUsername(req.Username)
if err != nil || !bcrypt.CompareHashAndPassword(user.PasswordHash, []byte(req.Password)) {
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "invalid credentials"})
}</p><p>// 签发 token,仅存必要字段
token, err := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"sub": user.ID, // subject —— 唯一用户标识
"exp": time.Now().Add(time.Hour * 24).Unix(), // 必须设过期时间
}).SignedString([]byte(os.Getenv("JWT_SECRET")))</p><p>if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "token generation failed"})
}</p><p>return c.JSON(fiber.Map{"token": token})
}如何让 protected 路由真正受控?
别只在路由前加 jwt.New() 就以为万事大吉。默认配置下,它不会阻止未认证请求进入 handler —— 它只是解析 token 并挂载到 c.Locals("user")。你仍需在 handler 内检查 c.Locals("user") 是否存在,或用中间件统一拦截。
- 推荐方式:用
jwt.New()的SuccessHandler和ErrorHandler显式控制流程 - 若用
c.Locals("user"),注意它的类型是jwt.Token,要手动取Claims解析sub - 不要在 protected handler 里重复解析 token —— Fiber JWT 中间件已做过了
示例中间件写法:
protected := app.Group("/api")
protected.Use(jwt.New(jwt.Config{
SigningKey: []byte(os.Getenv("JWT_SECRET")),
ContextKey: "user",
ErrorHandler: func(c *fiber.Ctx) error {
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "missing or invalid token"})
},
}))
<p>protected.Get("/profile", func(c <em>fiber.Ctx) error {
user := c.Locals("user").(</em>jwt.Token)
claims := user.Claims.(jwt.MapClaims)
userID := uint(claims["sub"].(float64)) // 注意 float64 类型转换
// 后续查库加载用户数据...
return c.JSON(fiber.Map{"id": userID})
})cookie 方式传 token 怎么配才不踩坑?
很多前端(尤其 Vue/React SPA)倾向用 httpOnly cookie 存 token,而非 localStorage。但 Fiber JWT v3 默认不支持 cookie 提取,必须手动指定 TokenLookup。
- 前端用
fetch(..., { credentials: 'include' })才能带 cookie - 后端需设置
TokenLookup: "cookie:auth_token",并确保登录接口设置了 cookie - 登录成功后,用
c.Cookie(&fiber.Cookie{...})写入,HTTPOnly和Secure(HTTPS 环境下必须开)都要显式设
登录响应中写 cookie 的片段:
c.Cookie(&fiber.Cookie{
Name: "auth_token",
Value: token,
Expires: time.Now().Add(time.Hour * 24),
HTTPOnly: true,
Secure: true, // 生产环境务必为 true
SameSite: "Lax",
})Fiber 的 JWT 中间件本身不处理登出,也不自动刷新 token;这些都得你自己用数据库或 Redis 实现黑名单或双 token 模式。别指望加个中间件就搞定所有认证边界。


















