WebSocket握手时JWT校验必须在c.Upgrade()前完成,通过请求头或查询参数提取token并同步验证,失败则返回401;成功后将用户信息存入context,并注意Origin校验与CORS配置。

WebSocket握手时无法直接用Echo中间件校验JWT
Echo的 MiddlewareFunc 默认只作用于HTTP请求,而WebSocket升级(Upgrade)是通过一次HTTP请求触发的,但后续通信完全脱离HTTP生命周期——中间件在 conn.ReadMessage() 阶段已不生效。所以不能把JWT校验逻辑塞进常规路由中间件里。
必须在Upgrade前完成JWT解析和验证
关键动作发生在调用 c.Upgrade() 之前:从请求头(通常是 Authorization: Bearer <token></token>)或URL查询参数中提取token,用你的密钥/公钥同步验证签名、过期时间、aud/iss等声明。验证失败就直接返回HTTP 401,阻止升级。
常见错误现象:websocket: bad handshake 或客户端收不到upgrade响应,往往是因为你在 Upgrade 后才去校验,此时连接已切换协议,HTTP状态码无法再写回。
实操建议:
- 用
c.Request().Header.Get("Authorization")提取token,手动切掉Bearer前缀 - 使用
jwt.Parse()(如 github.com/golang-jwt/jwt/v5)同步解析,**不要用 goroutine 异步校验**——Upgrade必须阻塞等待结果 - 验证失败时调用
c.JSON(401, echo.Map{"error": "invalid token"})或直接c.String(401, "Unauthorized"),然后return - 若token来自query(如
?token=xxx),用c.QueryParam("token"),但注意URL参数更易泄露,不推荐生产环境使用
校验通过后把用户信息注入WebSocket连接上下文
JWT校验成功后,你通常需要在后续消息处理中识别用户身份。Echo本身不提供WebSocket连接级context透传机制,得自己挂载。
Echo框架 5.1.0 版本源码包下载,适合关注 RealIP 行为变化、StartConfig.Listener、NewDefaultFS 和观测性中间件入口的开发团队。
推荐做法是:把解析出的 *jwt.Token 或自定义的 UserID、Roles 等字段存入 c.Request().Context(),再传给WebSocket handler:
// 校验成功后
claims := token.Claims.(jwt.MapClaims)
ctx := context.WithValue(c.Request().Context(), "user_id", claims["sub"])
c.SetRequest(c.Request().WithContext(ctx))
// 然后 Upgrade
ws, err := upgrader.Upgrade(c.Response(), c.Request(), nil)
if err != nil {
return
}
// 启动读写goroutine时,从conn关联的context里取 user_id
注意:别把原始token或完整claims塞进context,只放必要字段;同时确保 upgrader 实例是全局复用的(如 echo.WrapHandler(http.HandlerFunc(...)) 不适用,得用原生 websocket.Upgrader)。
别忽略CORS和Origin校验
JWT校验只是身份环节,WebSocket还面临跨域风险。如果前端不在同域调用,Upgrader.CheckOrigin 必须显式放行,否则即使JWT有效也会被拒绝升级。
常见疏漏点:
- 忘记设置
upgrader.CheckOrigin = func(r *http.Request) bool { return true }(开发时),或更安全地校验r.Header.Get("Origin") - 在Nginx反代场景下,客户端真实Origin可能被覆盖,需配置
proxy_set_header Origin $http_origin - 浏览器控制台报
Error during WebSocket handshake: Unexpected response code: 403,大概率是Origin拦截而非JWT问题
真正麻烦的是:JWT校验、Origin校验、TLS终止位置(边缘还是应用层)、以及WS连接复用之间容易互相掩盖错误源。建议先关掉所有校验,逐个打开并观察具体哪一步断开连接。

















