keyauth中间件默认从authorization头读取密钥,格式须为bearer xxx;若客户端使用x-api-key或apikey等非标准格式,需通过keylookup配置指定提取位置,否则校验失败。

KeyAuth中间件默认从哪个位置读密钥
默认读 Authorization 头,且期望格式是 Bearer xxx,不是 X-Api-Key 或 apikey。如果你的客户端发的是 Authorization: ApiKey abc123 或 Header: apikey: abc123,不改配置就永远校验失败。
关键点在于:KeyLookup 配置决定提取逻辑,它是一个字符串,格式为 "<source>:<name>"</name></source>,比如:
-
"header:X-Api-Key"→ 从请求头取X-Api-Key字段值 -
"header:Authorization"→ 取整个Authorization头,再由AuthScheme切分(默认"Bearer") -
"query:api_key"→ 从 URL 查询参数取api_key值
注意大小写:HTTP 头名本身不区分大小写,但 KeyLookup 中的 <name></name> 必须与实际传入的字段名**完全一致**(Echo 内部用 http.CanonicalHeaderKey 转换,但提取时仍按字面匹配)。
Validator函数里不能硬编码密钥
密钥校验逻辑必须异步安全、低延迟,且不能直接用 == 比较——否则可能被时序攻击利用。生产环境必须用恒定时间比较函数。
示例中常见的错误写法:
e.Use(middleware.KeyAuth(func(key string, c echo.Context) (bool, error) {
return key == "my-secret-key", nil // ❌ 危险!易受时序攻击
}))
正确做法(Go 标准库已提供):
- 用
crypto/subtle.ConstantTimeCompare(需先转[]byte) - 或更推荐:用
golang.org/x/crypto/argon2/bcrypt存哈希,查库比对哈希值,而非明文 - 若只是开发调试,至少用
strings.EqualFold+ 随机延迟兜底(不推荐上线)
另外,Validator 函数执行超时会影响整个请求吞吐,建议控制在 3–5ms 内;涉及 DB 查询务必加缓存(如 sync.Map 或 Redis)。
Echo框架 5.1.0 版本源码包下载,适合关注 RealIP 行为变化、StartConfig.Listener、NewDefaultFS 和观测性中间件入口的开发团队。
KeyAuth中间件必须注册在路由之前
和所有 Echo 中间件一样,e.Use() 必须在 e.GET()、e.POST() 等路由注册**之前**调用,否则不会生效。常见错误是把 KeyAuth 放在 e.Group() 内部却忘了加 .Use(),或者误写成 e.GET("/api", middleware.KeyAuth(...), handler) —— 这样只对单个路由生效,且顺序错乱可能导致 c.Request().Header 已被修改。
正确结构:
e := echo.New()
e.Use(middleware.KeyAuthWithConfig(middleware.KeyAuthConfig{
KeyLookup: "header:apikey",
Validator: func(key string, c echo.Context) (bool, error) {
// 校验逻辑
return valid, nil
},
}))
e.GET("/api/data", dataHandler) // ✅ 生效
如果只想保护部分路由,用 e.Group() 隔离:
api := e.Group("/api")
api.Use(middleware.KeyAuth(...))
api.GET("/users", usersHandler)
api.POST("/posts", postsHandler)
400 和 401 响应的区别很关键
KeyAuth 中间件对两种失败情况返回不同状态码,这个细节直接影响前端错误处理逻辑:
- 请求没带密钥 →
400 Bad Request(KeyLookup提取结果为空) - 密钥存在但校验失败 →
401 Unauthorized(Validator返回false)
别指望前端统一捕获 401 就重登——很多情况其实是漏传 header 导致 400,这时候该提示“请检查请求头是否包含 apikey”,而不是跳登录页。调试时用 curl -v 看响应头和状态码,比看日志更快定位是提取失败还是校验失败。
真正容易被忽略的是:KeyAuth 不会自动往 c.Set("key", ...) 注入密钥值,如果后续 handler 需要用到原始密钥(比如做租户路由),得自己在 Validator 里显式调用 c.Set(),否则拿不到。










