必须使用v3版本fiber-jwt,因v4未适配Fiber v2.50+上下文接口;注册需传[]byte型SigningKey,/login等路由须绕过;默认校验失败返回401,用户数据存于c.Locals["user"]。

JWT中间件注册必须用 v3 版本
fiber-jwt 的 v4 尚未适配 Fiber v2.50+ 的新上下文接口,直接 go get github.com/gofiber/jwt/v4 会导致编译错误:cannot use jwt.New() (value of type fiber.Handler) as fiber.Handler。必须锁定 v3:
go get github.com/gofiber/jwt/v3
注册时注意两点:SigningKey 必须是 []byte 类型;/login 等免鉴权路由不能被它拦截,否则登录接口自己就 401 了。
- 全局启用(保护所有非白名单路由):
app.Use(jwt.New(jwt.Config{SigningKey: []byte("your-secret")})) - 分组启用更安全:
api := app.Group("/api"); api.Use(jwt.New(...)); api.Get("/user", handler) - 若用环境变量管理密钥,记得提前
os.Getenv("JWT_SECRET")并做空值校验,否则运行时报 panic
鉴权失败后别调 next(c)
fiber-jwt 默认在 Token 无效时返回 401,但这个行为只覆盖解析和签名验证阶段。一旦进入业务 handler,权限校验就得你自己写——而最容易出错的是:校验不通过时仍调用了 next(c),导致后续逻辑继续执行,可能重复写响应、查 DB、甚至返回 200。
正确做法只有三步:
- 从
c.Locals("user")取出*jwt.Token,再断言为jwt.MapClaims - 检查 claims 中的
"role"或"permissions"字段是否满足要求 - 不满足 →
c.Status(403).SendString("forbidden")+return,绝对不碰next(c)
注意:c.Locals 是 Fiber 专为中间件传值设计的机制,比 c.Context().Value 更轻、更安全;用错地方就读不到数据。
用户信息该存什么、怎么取
JWT 解析成功后,payload 默认存进 c.Locals["user"],类型是 *jwt.Token。你得手动解出 claims,并且建议只存轻量结构体或 jwt.MapClaims,别存原始 token 字符串——既浪费内存,又可能意外泄露。
典型取值写法:
func adminHandler(c *fiber.Ctx) error {
user := c.Locals("user").(*jwt.Token)
claims := user.Claims.(jwt.MapClaims)
userID := uint64(claims["id"].(float64))
role := claims["role"].(string)
if role != "admin" {
return c.Status(403).SendString("no perm")
}
// ... business logic
}
如果 payload 里塞了嵌套结构(比如 {"user": {"id": 1, "name": "a"}}),断言时要多一层,别直接 claims["user"].(map[string]interface{}) —— Go 的类型系统不会自动展开,容易 panic。
OPTIONS 和健康检查路径必须显式放行
Fiber 的路由匹配基于 c.Route().Path 模板,不是原始 URL。如果你在鉴权中间件里写 if c.Path() == "/health",会漏掉带查询参数的请求(如 /health?full=1)。更可靠的方式是匹配路由模板:
- 先在注册路由时打上标签:
app.Get("/health", healthHandler).Name("health") - 鉴权中间件里判断:
if c.Route().Name == "health" || c.Route().Path == "/api/docs" || c.Method() == "OPTIONS" { return next(c) } - 别依赖
c.Path()做路径白名单,它不标准化,尤其在有通配符路由(如/users/:id)时行为不可靠
这个细节在调试 CORS 预检失败或 k8s readiness probe 503 时特别关键——很多人卡在这里半天,最后发现只是 OPTIONS 被鉴权拦死了。


















