fiber 没有内置 appkey 校验中间件,必须用 fiber.keyauth() 手动集成并显式配置 extractor 和 validator;若校验含 db 查询或外部调用,须控制在 5ms 内,否则拖慢所有请求。

直接说结论:Fiber 没有内置 AppKey 校验中间件,必须用 fiber.KeyAuth() 手动集成,并显式配置 Extractor 和 Validator;若校验逻辑含数据库查询或外部调用,务必控制在 5ms 内,否则会拖慢所有请求。
为什么不能直接用默认 KeyAuth 中间件
默认的 fiber.KeyAuth() 只做最简匹配——它从 X-Api-Key Header 提取字符串,然后比对一个固定值(比如 "secret123")。真实业务中 AppKey 是动态的,需查数据库、校验状态、检查配额,这些都得自己写进 Validator 函数里。不重写 Validator,就只是个静态口令校验器,毫无业务意义。
怎么写一个带 DB 查询的 AppKey 校验中间件
核心是把密钥验证逻辑塞进 Validator 字段,同时注意上下文复用和错误处理:
-
Validator函数签名必须是func(fiber.Ctx, string) (bool, error),第二个参数是提取到的 key 字符串 - DB 查询要快:建议用
appkey字段建唯一索引,SELECT id, status, quota_used FROM apps WHERE appkey = ? AND status = 'active' - 别在
Validator里调c.Status()或c.Send()—— 这里只负责“通过/不通过”,错误响应由中间件内部统一处理 - 失败时返回
keyauth.ErrMissingOrMalformedAPIKey(来自github.com/gofiber/fiber/v3/middleware/keyauth),Fiber 会自动返回 401
示例片段:
validator := func(c fiber.Ctx, appKey string) (bool, error) {
var app AppModel
err := db.QueryRow("SELECT id, quota_used FROM apps WHERE appkey = ? AND status = 'active'", appKey).Scan(&app.ID, &app.QuotaUsed)
if err != nil {
return false, keyauth.ErrMissingOrMalformedAPIKey // 不暴露是不存在还是被禁用
}
if app.QuotaUsed >= 1000 {
return false, keyauth.ErrMissingOrMalformedAPIKey
}
c.Locals("app_id", app.ID) // 后续 handler 可用
return true, nil
}
AppKey 提取位置不固定?用 Chain 提取器兜底
客户端可能把 AppKey 放在 Header、Query 或 Form 里,不能只盯死一个位置。用 extractors.Chain() 组合多个提取方式,按顺序尝试:
- 优先从 Header 的
X-App-Key字段取(大小写敏感,别写成X-Appkey) - Header 没有就看 Query 参数
app_key - 最后 fallback 到 Form 表单字段
app_key
配置写法:
keyauth.New(keyauth.Config{
Validator: validator,
Extractor: extractors.Chain(
extractors.FromHeader("X-App-Key"),
extractors.FromQuery("app_key"),
extractors.FromForm("app_key"),
),
})
容易被忽略的三个点
一是 app.Use() 必须放在所有路由注册之前,否则中间件压根不执行;二是 Validator 函数里如果用了 db.QueryRow 却没加 context 超时控制,一次慢查询会让整个中间件卡住;三是校验通过后,务必将 app_id 或其他必要信息存到 c.Locals,而不是全局变量或闭包捕获——*fiber.Ctx 是复用对象,下个请求会覆盖内容。











