WebSocket鉴权必须在upgrader.Upgrade()前完成,从r.Header.Get("Authorization")、r.URL.Query().Get("token")或r.Cookie()提取凭据并同步校验JWT(检查exp/iat/nbf/签名),失败立即http.Error返回401;禁止在CheckOrigin或升级后处理,避免非法连接建立。

WebSocket握手时如何获取并校验Token
Go 的 gorilla/websocket 不在升级连接后才开始处理逻辑,而是在 Upgrader.Upgrade() 调用前,就允许你读取原始 HTTP 请求(包括 Header、URL Query 和 Cookie)。这是做鉴权的唯一可靠时机——一旦调用 Upgrade(),HTTP 连接就转为 WebSocket 帧流,再也拿不到原始请求头了。
常见错误是试图在升级后的连接里解析 Token(比如读第一帧 JSON),这不仅破坏协议规范,还让恶意客户端绕过鉴权(发个空帧或非法帧直接卡死服务)。
- 从
r.URL.Query().Get("token")提取 URL 参数(适合调试或简单场景) - 从
r.Header.Get("Authorization")读 Bearer Token(推荐,符合 REST 习惯) - 从
r.Header.Get("Cookie")解析并提取自定义 Cookie 字段(需注意 SameSite 和 HttpOnly 限制) - 校验失败时,**必须直接返回 HTTP 错误响应**,且不能调用
upgrader.Upgrade();否则会触发 “http: response.WriteHeader on hijacked connection” panic
gorilla/websocket Upgrader.CheckOrigin 不能替代鉴权
CheckOrigin 只控制跨域来源(Origin header),它发生在更早阶段,且不暴露完整请求对象(只有 *http.Request 指针,但某些字段如 Body 已不可读)。它设计初衷是防止 CSRF 类攻击,不是身份验证入口。
很多人误把 Token 校验逻辑塞进 CheckOrigin,结果发现 r.Header.Get("Authorization") 总是空——因为浏览器在 WebSocket 握手时默认不发送 Authorization 头,除非显式配置;而 CheckOrigin 又没机会做重定向或写响应体。
立即学习“go语言免费学习笔记(深入)”;
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 永远不要在
CheckOrigin里做 Token 解析、数据库查询或 JWT 验证 - 如果需要 Origin 白名单 + Token 鉴权,两个逻辑要分开:先在 Upgrade 前校验 Token,再让
CheckOrigin做纯域名判断 -
CheckOrigin返回 false 时,Upgrade()会返回websocket.ErrBadHandshake,但错误响应状态码固定为 403,无法自定义提示
如何安全传递和验证 JWT Token
Token 应该是短期有效的、带签名校验的 JWT,并绑定客户端 IP 或 User-Agent(可选),避免泄露后长期滥用。服务端验证必须检查 exp、iss、签名算法(禁止 none 算法)、以及是否在吊销列表中(如 Redis 缓存黑名单)。
注意:jwt.Parse 默认不校验 exp,必须显式传入 jwt.WithValidMethods 和 jwt.WithExpirationRequired() 等选项,否则过期 Token 仍能通过。
- 使用
github.com/golang-jwt/jwt/v5,避免老版本 v3/v4 的已知绕过漏洞 - 密钥必须从环境变量或 secret manager 加载,禁止硬编码在代码里
- 验证通过后,建议把用户 ID 或角色信息存入
context.WithValue(),后续 WebSocket 消息处理可复用,避免重复查库 - 不要把完整 Token 存进 WebSocket 连接结构体——内存泄漏风险高,且无自动过期机制
客户端连接时忘记带 Token 导致的静默失败
前端用 new WebSocket(url) 时,无法设置 Authorization header(浏览器限制),所以必须用 query 参数或 cookie 方式传 Token。但 query 方式有长度限制、日志泄露风险;cookie 方式又依赖同源策略和 withCredentials: true 设置。
典型现象:WebSocket 连接立即关闭,Chrome DevTools Network 面板显示状态码 401/403,但控制台只报 WebSocket connection to 'ws://...' failed,没有具体原因。
- 服务端应在拒绝连接时,往 HTTP 响应体写明错误(如
w.Write([]byte("invalid token"))),虽然浏览器不显示,但可用 curl 或 Postman 复现调试 - 前端连接前先发一次 HTTP GET 检查 Token 是否有效,成功后再建 WS 连接(增加 RTT,但可观测性更好)
- 如果用 Nginx 做反向代理,确认
proxy_set_header Upgrade $http_upgrade;和proxy_set_header Connection "upgrade";已配置,否则 Upgrade 请求会被降级成普通 HTTP
真正麻烦的不是写不出鉴权逻辑,而是忘记鉴权发生在 HTTP 阶段而非 WebSocket 阶段;一旦连上,就再也没机会拦住非法连接了。

















