WebSocket 连接中使用 JWT Token 进行授权的正确方式

12次阅读

WebSocket 连接中使用 JWT Token 进行授权的正确方式

websocket 客户端需将 jwt Token 通过 `authorization: bearer ` 请求头传递,而非 url 查询参数;服务端(go)默认仅从请求头或表单字段解析 token,不自动读取查询字符串中的 `access_token` 参数。

websocket 协议中,连接建立阶段本质上是 http Upgrade 请求(即 GET 请求携带 Upgrade: websocket 头),因此客户端可且应当像普通 HTTP 请求一样,在请求头中携带认证信息。而你当前的实现——将 token 拼入 URL 查询参数(如 ?token=xxx 或 ?access_token=xxx)——虽语法合法,但无法被 jwt-go 的 ParseFromRequest 函数识别,原因如下:

? 为什么查询参数不生效?

ParseFromRequest 的源码逻辑明确只检查两个位置:

  1. Authorization 请求头(格式为 Bearer );
  2. 已解析的表单数据(req.Form.Get(“access_token”)),这要求请求是 multipart/form-data 或 application/x-www-form-urlencoded 类型,并完成 req.ParseMultipartForm() 或 req.ParseForm()。

⚠️ 关键点:WebSocket 握手请求是纯 HTTP GET,不包含请求体,也不触发自动表单解析。即使你在 URL 中添加 ?access_token=xxx,req.Form 默认为空(除非显式调用 req.ParseForm()),且 ParseFromRequest 并未尝试从 req.URL.Query() 中提取参数。

✅ 正确做法:通过请求头传递 Token

使用支持自定义 header 的 WebSocket 客户端库(如 websocket-client),在握手请求中注入 Authorization 头:

from websocket import create_connection  def test_auth_token(token, ip, port, uuid):     url = f"ws://{ip}:{port}/{uuid}"     headers = [f"Authorization: Bearer {token}"]  # ✅ 关键:Bearer + 空格 + token     conn = create_connection(url, header=headers)     try:         result = conn.recv()         print("Connected and received:", result)         assert result is not None     finally:         conn.close()

? 注意:header 参数接受字符串列表,每项为 “Key: Value” 格式;Authorization 头值必须严格为 Bearer (注意大小写与空格)。

?️ 服务端补充建议(Go)

确保你的 Go WebSocket 服务在 Upgrade 前对原始 HTTP 请求进行 token 验证。例如(使用 gorilla/websocket):

var upgrader = websocket.Upgrader{     CheckOrigin: func(r *http.Request) bool {         // 在 Upgrade 前验证 token         token, err := jwt.ParseFromRequest(r, func(token *jwt.Token) (interface{}, error) {             return []byte("your-secret-key"), nil // 实际应使用公钥或安全密钥管理         })         if err != nil || !token.Valid {             return false         }         // 可选:将用户信息存入 context 或 session         return true     }, }

若需更灵活的 token 提取(如支持查询参数),可自行封装解析逻辑,但不推荐——因查询参数易被日志、代理、cdn 缓存泄露,存在安全风险;Header 是标准、安全且符合 JWT 最佳实践的方式。

✅ 总结

  • ❌ 错误:ws://host/path?access_token=xxx → ParseFromRequest 忽略 URL 查询。
  • ✅ 正确:create_connection(url, header=[“Authorization: Bearer xxx”]) → Header 被 ParseFromRequest 优先识别。
  • ? 安全提示:永远避免在 URL 中传递敏感凭证;JWT 应始终通过 Authorization 头传输。
  • ? 调试技巧:在 Go 服务端打印 req.Header 和 req.URL.Query(),确认 token 实际到达位置。

text=ZqhQzanResources