func authmiddleware(c *fiber.ctx) error { token := c.get("authorization"); if token == "" || !strings.hasprefix(token, "bearer ") { return c.status(fiber.statusunauthorized).json(fiber.map{"error": "invalid token"}) }; user, err := validatejwt(strings.trimprefix(token, "bearer ")); if err != nil { return c.status(fiber.statusunauthorized).json(fiber.map{"error": "invalid token"}) }; c.locals("user_id", user.id); c.locals("role", user.role); return nil }

直接写一个符合 func(*fiber.Ctx) error 签名的函数就行,但必须处理好凭证提取、验证失败响应、上下文传递这三件事,否则会静默跳过或 panic。
中间件函数签名和返回值必须严格匹配
Fiber 所有中间件都要求函数签名是 func(c *fiber.Ctx) error。返回 nil 表示继续执行后续中间件或 handler;返回非 nil error(如 c.Status(401).SendString("Unauthorized"))表示中断流程。
- 不能漏掉
*fiber.Ctx指针参数,写成func(c fiber.Ctx)会编译失败 - 不能返回
string或bool,Fiber 运行时无法识别,直接 panic - 若验证失败后想终止请求,推荐用
c.SendStatus(401)或c.JSON(fiber.Map{"error": "invalid token"}),再显式 return error
从请求中安全提取凭证的常见方式
别硬写 c.Get("Authorization") —— 客户端可能把 token 放在 query、cookie 或自定义 header,且大小写/前缀格式不统一。
- Bearer Token:用
c.Get("Authorization")后手动切分,检查是否以"Bearer "开头,再取后半段 - API Key:用
c.Get("X-Api-Key")或c.Query("key"),注意生产环境避免用 query 传密钥 - Cookie Token:用
c.Cookies("session_id"),需确保 Cookie 设置了HttpOnly和Secure - 提取失败时不要直接 panic,应统一返回 400 或 401,并记录日志(如
log.Printf("missing auth header from %s", c.IP()))
验证逻辑要隔离、可测、低延迟
认证中间件里别塞数据库查询或远程 HTTP 调用——它会在每个请求都执行,高并发下容易拖垮服务。
- JWT 验证推荐用
github.com/golang-jwt/jwt/v5解析并校验 signature/exp/iss,密钥从 config 加载,别硬编码 - API Key 查表验证,建议加 Redis 缓存(TTL 与 key 生命周期一致),缓存未命中再查 DB
- 验证耗时应稳定在 5ms 内,超时可考虑降级为白名单兜底,或记录慢日志告警
- 验证通过后,务必把用户 ID、角色等信息写入
c.Locals(如c.Locals("user_id", 123)),让下游 handler 安全读取
绑定到路由时必须注意作用域和顺序
中间件不是挂上就生效——它只对“之后注册”的路由起作用,且执行顺序完全由代码书写顺序决定。
- 全局认证(如所有
/api/*)用api := app.Group("/api", authMiddleware),别用app.Use(authMiddleware),否则静态资源也被拦 - 单个敏感路由叠加多层控制(如 JWT + IP 白名单),写成
api.Delete("/user", jwtAuth, ipWhitelist, handler) - 如果用了
app.Use(logger.New()),它必须放在所有认证中间件之前,否则日志里看不到原始请求头 - 测试时用
curl -H "Authorization: Bearer xxx" http://localhost:3000/admin验证,别只跑单元测试忽略 header 传递链
最常被忽略的是 c.Locals 的生命周期——它只在当前请求上下文有效,跨 goroutine 会丢失;还有 JWT 的 exp 校验必须用 time.Now().UTC(),服务器时区错位会导致大量 token 提前失效。











