在 gorilla websocket 应用中,应在 http 升级为 websocket 连接前完成身份验证(如 jwt 校验、session 验证等),而非在 websocket 连接建立后处理;此举可有效防止会话劫持,安全性与常规 http 请求一致。
WebSocket 本身是基于 HTTP 的协议升级(Upgrade: websocket),其连接建立过程始于一个标准的 HTTP 请求。因此,所有认证逻辑必须在 Upgrader.Upgrade() 调用之前完成——这是保障认证安全性的核心原则。一旦升级完成,连接即脱离 HTTP 生命周期,不再经过中间件或认证钩子,此时再做鉴权已无意义,也无法阻止恶意客户端复用他人凭证发起连接。
以下是一个使用 JWT Token 认证的典型示例(结合 Gorilla/mux 和 gorilla/websocket):
func wsHandler(w http.ResponseWriter, r *http.Request) {
// 1. 从查询参数或 Header 提取 token(推荐 Authorization: Bearer )
tokenStr := r.Header.Get("Authorization")
if tokenStr == "" {
http.Error(w, "missing auth token", http.StatusUnauthorized)
return
}
if strings.HasPrefix(tokenStr, "Bearer ") {
tokenStr = strings.TrimPrefix(tokenStr, "Bearer ")
}
// 2. 解析并校验 JWT(使用 github.com/golang-jwt/jwt/v5 等库)
token, err := jwt.Parse(tokenStr, func(t *jwt.Token) (interface{}, error) {
return []byte(os.Getenv("JWT_SECRET")), nil
})
if err != nil || !token.Valid {
http.Error(w, "invalid or expired token", http.StatusUnauthorized)
return
}
// 3. (可选)从 token.Claims 中提取用户 ID 或角色,存入 context
claims, ok := token.Claims.(jwt.MapClaims)
if !ok {
http.Error(w, "invalid token claims", http.StatusUnauthorized)
return
}
userID := uint64(claims["user_id"].(float64))
// 4. ✅ 此时才安全地升级 WebSocket 连接
upgrader := websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
// 生产环境应严格校验 Origin(如白名单)
return true // 示例中放宽,实际请限制
},
}
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Print
f("Upgrade error: %v", err)
return
}
defer conn.Close()
// 5. 将认证后的用户信息关联到连接(例如存入 map 或使用结构体封装)
client := &Client{
ID: userID,
Socket: conn,
Send: make(chan []byte, 256),
}
// 启动读写协程...
} Go 中 WebSocket 认证的本质是「HTTP 认证前置」:把 WebSocket 连接视为一次特殊的 HTTP 请求,复用你已在 REST API 中验证过的认证方案(JWT、OAuth2、Session 等)。只要你在 Upgrade() 前完成了完整、可信的身份核验,后续的 WebSocket 通信就和受保护的 HTTP 请求一样安全——因为 WebSocket 并未引入新的网络层漏洞,其安全性完全继承自初始 HTTP 请求的上下文。