
websocket 客户端需将 jwt token 通过 `authorization: bearer
在基于 WebSocket 的实时通信场景中,安全地完成用户身份鉴权至关重要。许多开发者误以为可通过 URL 查询参数(如 ?token=xxx 或 ?access_token=xxx)向 WebSocket 服务端传递 JWT,但实际中——尤其是使用 dgrijalva/jwt-go(现维护分支为 golang-jwt/jwt)等标准库进行解析时——服务端验证逻辑默认不解析查询参数中的 token。
如问题中 Go 服务端代码所示,ParseFromRequest 函数仅检查两个位置:
- Authorization 请求头(格式为 Bearer
); - access_token 表单字段(需经 req.ParseMultipartForm() 或 req.ParseForm() 解析后的 req.Form)。
而 WebSocket 握手请求(HTTP Upgrade)不会自动将 URL 查询参数注入 req.Form,即使调用了 req.ParseMultipartForm(10e6),该方法也仅处理 multipart/form-data 类型的请求体(常见于文件上传),对 application/x-www-form-urlencoded 或纯 query string 无作用。因此 req.Form.Get("access_token") 始终为空,最终返回 ErrNoTokenInRequest。
✅ 正确做法:将 Token 放入 Authorization 请求头
使用 Python 客户端(如 websocket-client)时,应通过 header 参数显式传入认证头:
from websocket import create_connection
def test_auth_token(token):
# ✅ 正确:通过 Header 传递 Bearer Token
headers = ["Authorization: Bearer " + token]
ws_url = f"ws://:port/{container.uuid}"
conn = create_connection(ws_url, header=headers)
result = conn.recv()
assert result is not None
conn.close() ⚠️ 注意事项:
- Authorization 头值必须严格遵循 Bearer
格式(注意 Bearer 后有一个空格); - 确保 Token 未被 URL 编码(若手动拼接 URL,需避免对 token 二次编码;而 header 中无需编码);
- 若服务端使用较新版本 golang-jwt/jwt/v5,建议升级并配合 http.Request.Context() 进行更健壮的 token 提取与验证;
- 避免在 URL 中传递敏感 Token(易被日志、代理、浏览器历史记录泄露),Header 是更安全、符合规范的方式。
? 扩展建议:
服务端可增强兼容性,例如主动从 req.URL.Query().Get("access_token") 读取查询参数(需自行实现,非 jwt-go 默认行为)。但推荐客户端统一走 Header 方案,既符合 OAuth 2.0 / RFC 6750 规范,也便于与现有 HTTP API 鉴权逻辑保持一致。









