WebSocket连接前必须做JWT校验,否则IsWebSocket()无法拦截未授权连接;Fiber的keyauth中间件需配置FromQuery("token")等多方式提取Token,并在validateJWT中严格检查token.Valid字段,避免过期Token被误判有效。

WebSocket连接前必须做JWT校验,否则IsWebSocket()拦不住未授权连接
很多人以为只要在路由层加了keyauth中间件,WebSocket就自动被保护了——其实不是。Fiber的IsWebSocket()只是检测请求头是否含Upgrade: websocket,它不阻断握手过程。如果认证逻辑没在升级前完成,攻击者可直接发起WS连接并绕过所有Token校验。
正确做法是在keyauth.New()中用Next函数显式跳过非WS请求,同时确保Extractor能从Sec-WebSocket-Protocol、查询参数或请求头中取到token。常见错误是只配置了FromAuthHeader("Bearer"),但浏览器JS发起WS时无法携带Authorization头(受CORS和协议限制),必须补上FromQuery("token")。
-
Next函数里别写c.IsWebSocket() == false,要写!c.IsWebSocket(),Go里== false和!语义不同,前者可能因接口返回nil而panic - 如果前端用
new WebSocket("wss://...?token=xxx")传参,后端extractors.FromQuery("token")必须存在,且不能依赖FromAuthHeader兜底 - 校验失败时返回
keyauth.ErrMissingOrMalformedAPIKey,不要自己c.Status(401).SendString(),否则连接会卡在pending状态
validateJWT函数里必须手动调用jwt.Parse并检查Valid字段
Fiber的keyauth中间件只管“验证通过与否”,不负责解析结果。如果你在validateJWT里只调用jwt.Parse却没判token.Valid,会导致过期token被当成有效——因为jwt.Parse在过期时仍可能返回非nil的*jwt.Token,只是其Valid字段为false。
典型陷阱是直接用err != nil判断失败,但jwt.Parse对过期token返回的是jwt.ErrTokenExpired,属于非nil err,而对签名错误则返回jwt.ErrSignatureInvalid。这两类错误都该统一转成keyauth.ErrMissingOrMalformedAPIKey,避免暴露错误细节。
- 别把密钥硬编码成
"your-secret-key",生产环境必须从环境变量读取,比如os.Getenv("JWT_SECRET") - 解析成功后,务必执行
if !token.Valid { return false, ... },仅靠err == nil不够 - 用户信息存进
c.Locals("user")时,建议只存token.Claims里的map[string]interface{},别存原始*jwt.Token对象,避免内存泄漏
Access Token过期后,WebSocket连接不会自动刷新,得靠客户端重连+新Token
JWT本身无状态,服务端不存Token生命周期,所以WebSocket长连接一旦建立,后续帧通信不再触发validateJWT。这意味着:就算Access Token已过期,只要连接没断,消息照收照发。真正的鉴权边界只在握手那一刻。
解决方案不是让服务端“续期”,而是要求客户端在Token快过期前主动关闭旧连接、用新Token建新连接。后端需在握手响应里透出Token剩余有效期(比如通过Sec-WebSocket-Protocol头或首次发送的控制帧),否则前端无法预判重连时机。
- 别在
validateJWT里尝试解析exp时间戳再对比当前时间——jwt.Parse已内置校验,重复判断多余 - 如果业务需要实时感知Token过期,只能靠客户端定时轮询
/auth/refresh接口,或服务端在WebSocket消息里嵌入过期提醒(但需约定协议格式) - Refresh Token机制不适合WebSocket场景,因为WS协议不支持标准HTTP重定向或Set-Cookie,强行塞进自定义头易被代理截断
Fiber v3的keyauth中间件不兼容jwt.Parse v5的func(*jwt.Token) (interface{}, error)签名
Fiber v3文档示例用的是jwt/v4,但很多项目已升到jwt/v5。v5把密钥回调函数签名改成了func(token *jwt.Token) (any, error),而Fiber v3的keyauth源码里仍按v4的interface{}类型断言——这会导致运行时报panic: interface conversion: any is []uint8, not []byte之类错误。
临时解法是降级到github.com/golang-jwt/jwt/v4,长期方案是等Fiber官方适配。若坚持用v5,必须手动改写验证逻辑,绕过keyauth中间件,改用app.Use(func(c fiber.Ctx) error { ... })自行处理。
- 检查
go.mod里github.com/golang-jwt/jwt的版本,v4末版是v4.5.0,v5首版是v5.0.0 - 用v5时,密钥回调必须返回
any(即interface{}),但实际返回[]byte会被v5内部转成[]uint8,导致类型不匹配 - 最稳的做法:删掉
middleware/keyauth依赖,自己写一个轻量中间件,只做Extract → Parse → Validate → Locals四步
Valid字段,而框架版本错配比逻辑错误更难排查。


















