fiber 的 fiberjwt.new 不自动将 jwt claims 注入 context.context,需显式配置 claimsfield 并手动挂载;安全提取用户信息应先检查 ctx.locals("user") 类型与字段存在性,数字字段为 float64,role 可能为数组,须谨慎断言。

为什么 Fiber 的 fiberjwt.New 不能直接用 ctx.UserContext() 拿用户信息
因为 fiberjwt.New 默认只做 token 校验和解析,不自动把 claims 注入到 context.Context 中。你调用 ctx.UserContext() 得到的仍是空 context,不是解析后的用户数据。
正确做法是:在中间件配置里显式启用 ClaimsField,并指定一个 key(比如 "user"),它会把解析出的 jwt.MapClaims 存进 fiber.Ctx.Locals,再手动挂载到 context。
- 必须设置
ClaimsField: "user",否则Locals里没有字段可取 - 要在中间件之后、路由 handler 里手动执行
ctx.Context().WithValue(...),Fiber 不自动帮你做这步 - 别依赖
ctx.UserContext()返回已填充的用户——它只是 Fiber 内部用的底层 context,不是你塞进去的那个
如何从 fiber.Ctx 安全提取 JWT 用户 ID 和角色
最稳妥的方式是先检查 ctx.Locals("user") 是否存在且为 jwt.MapClaims 类型,再按需取字段。不要直接断言或强制类型转换,否则 panic 风险高。
// 示例:在受保护路由中提取 user_id 和 role
if claims, ok := ctx.Locals("user").(jwt.MapClaims); ok {
if uid, ok := claims["user_id"].(float64); ok {
userID := int64(uid) // JWT 数字默认是 float64
role := claims["role"].(string)
// 后续业务逻辑...
}
}
-
user_id这类数字字段在 JWT 解析后是float64,不是int64或string,强转前务必检查类型 -
role如果是数组(如["admin", "editor"]),claims["role"]是[]interface{},需额外遍历转换 - 永远先判空再取值,
claims["xxx"]可能是nil,尤其当 token 签发时漏填字段
fiberjwt.Config 里哪些参数改了会影响 token 刷新和过期行为
JWT 本身无状态,所谓“刷新”完全靠业务逻辑控制。但 fiberjwt.Config 中几个字段直接影响校验结果和错误响应,间接决定你能否触发刷新流程:
-
SigningKey必须和签发时完全一致(包括类型:[]byte / string / interface{}),否则TokenExpired错误可能被误判为InvalidToken -
ContextKey只影响ctx.Locals存储 key 名,和过期无关;但若和ClaimsField不一致,会导致取不到 claims -
TokenLookup若设成"header: Authorization",就无法从 cookie 或 query 获取 token,前端换方式传 token 时会直接 401 -
AuthScheme默认"Bearer",如果前端传"Bearer xxx"却配成"JWT",解析直接失败,连过期判断都走不到
为什么用 ctx.Next() 后还能在后续中间件里读到 Locals("user")
因为 fiber.Ctx.Locals 是请求生命周期内共享的 map,不是作用域隔离的变量。只要前面的中间件写入了 ctx.Locals("user"),后面所有中间件和 handler 都能读到——前提是没被覆盖或清空。
- 不需要
return或特殊传递,Locals天然跨中间件 - 但如果某个中间件执行了
ctx.Locals = make(map[string]interface{}),之前存的数据就丢了(极少见,但自定义中间件时要注意) - 别混淆
Locals和State:State是整个 app 生命周期共享的,Locals才是单请求专用
真正容易被忽略的是:token 解析成功 ≠ 用户合法。中间件只管签名和时效,查数据库验证账号是否禁用、权限是否变更,得你自己在 handler 里补这层校验。











