websocket连接建立时必须在upgrade前通过r.url.query()或r.header.get()提取并校验认证凭据,失败返回401/403;鉴权通过后将用户信息封装进自定义authconn结构体,避免升级后校验或全局映射。

WebSocket连接建立时如何传入认证凭据
Go 的 net/http 本身不拦截 WebSocket 升级请求的 query 或 header,但客户端发起连接时能带 Authorization header 或 query 参数(如 ?token=xxx),服务端必须在 http.HandlerFunc 中提前读取并验证,**不能等到 websocket.Upgrader.Upgrade() 之后再鉴权**——因为此时 HTTP 连接已切换为 WebSocket 协议,header 和 query 不再可访问。
常见错误是把 token 解析逻辑写在升级后的 conn 上下文里,导致无法拒绝非法连接。
- 推荐方式:从
r.URL.Query().Get("token")或r.Header.Get("Authorization")提取凭据 - 务必在调用
upgrader.Upgrade()前完成校验,失败直接http.Error() - 注意:浏览器
WebSocket构造函数不支持自定义 header,所以 query 传参更通用;若用fetch+ws库或非浏览器客户端,可用 header
使用 JWT 在 Upgrade 前完成快速校验
JWT 适合无状态鉴权,但要注意解析开销和密钥轮换。别在每次连接都查数据库——除非你同时需要实时封禁或权限变更同步。
关键点在于校验时机和错误反馈:
- 用
github.com/golang-jwt/jwt/v5的ParseWithClaims(),配合自定义jwt.Claims结构体提取user_id、exp、scope - 必须检查
err == nil && token.Valid,仅判err会漏掉过期但签名有效的 token - 校验失败时返回标准 HTTP 状态码:
http.StatusUnauthorized(401)或http.StatusForbidden(403),不要返回 200 + 错误消息 - 避免在 token 中塞大量权限字段,连接建立后可通过
conn.WriteMessage()主动推送权限变更通知
升级后如何绑定用户身份与 WebSocket 连接
鉴权通过后,需把用户信息(如 user_id)安全地挂载到连接生命周期中。别用全局 map 存 conn → user 映射——并发读写要加锁,且易内存泄漏。
更稳妥的做法是封装一个带上下文的连接结构:
type AuthConn struct {
*websocket.Conn
UserID uint64
Expires time.Time
}
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
token := r.URL.Query().Get("token")
userID, exp, err := parseAndValidateToken(token)
if err != nil {
http.Error(w, "invalid token", http.StatusUnauthorized)
return
}
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
return
}
authConn := &AuthConn{
Conn: conn,
UserID: userID,
Expires: exp,
}
// 后续业务逻辑用 authConn.UserID,而非反复解析 token
}
这样既避免重复解析,又防止未授权连接冒用身份——因为 UserID 是 Upgrade 前确定的,不可篡改。
为什么不能在 WebSocket 消息层做主鉴权
有人习惯在 conn.ReadMessage() 后检查每个消息里的 token 字段,这是严重设计缺陷:它允许未授权客户端成功建立长连接,占用服务器 fd、内存和 goroutine,还可能触发重连风暴。
真实风险包括:
- 攻击者用脚本批量建连,耗尽
ulimit -n,导致新连接失败 - 心跳包(ping/pong)照常收发,监控难以发现异常连接
- 权限校验逻辑分散,容易漏掉某类消息路径(比如二进制帧没校验)
- 无法按用户维度限频(如每秒最多 5 条消息),因连接已建立才开始计数
真正的鉴权边界只有一个:HTTP 升级那一刻。之后所有消息都默认可信(前提是传输层加密且连接未被劫持),复杂权限控制应下沉到业务路由层,而不是每条消息都 decode + validate。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











