workerman websocket鉴权必须在onwebsocketconnect回调中完成,通过url参数传递短期token并校验,失败立即close;需设超时和心跳,禁止在onmessage或onconnect中鉴权。

Workerman WebSocket 连接鉴权必须在握手阶段完成
不能等 onMessage 或 onOpen 之后再校验——此时连接已建立,TCP 和 WebSocket 协议层资源(文件描述符、内存)已被占用,恶意客户端可发起空连接耗尽服务。Workerman 的 onConnect 是 TCP 层回调,不适用于 WebSocket 鉴权;真正有效的入口是 onWebSocketConnect 回调,它在 HTTP 握手完成、协议升级为 WebSocket 后立即触发,且尚未进入业务消息循环。
常见错误现象:
- 在
onMessage里解析{"type": "login", "token": "..."}—— 风险已产生 - 把鉴权逻辑写在
onConnect里 —— 此时$http_header不可用,拿不到 URL 参数或 header - 没设超时直接等待 token —— 客户端不发 token 就一直挂着连接
用 URL 参数传短期 Token 是最兼容的方案
浏览器原生 WebSocket 构造函数只接受 URL 字符串,无法设置 header,所以把 token 放 query string 是唯一通用方式。后端需从 $http_header['request_uri'] 或更稳妥地从 $_GET(Workerman 自动解析)中提取。
实操要点:
- 前端必须用
encodeURIComponent(token)编码,否则含.、+、/的 JWT 会破坏 URL 结构 - Token 必须是短期专用的,比如调用
/api/v1/ws-token换取 15 分钟有效期的 JWT,别直接用登录态长期 token - 后端解析后必须校验
exp、签名、iss,并查 Redis 确认未被吊销(如用户主动登出) - 校验失败立刻调用
$connection->close(4001, "Unauthorized"),不要send()错误消息
示例代码片段(放在 onWebSocketConnect 内):
$token = $_GET['token'] ?? '';
if (!$token || !$jwt->validateToken($token)) {
return $connection->close(4001, 'Unauthorized');
}
$payload = $jwt->decodeToken($token);
if (empty($payload['user_id']) || time() > ($payload['exp'] ?? 0)) {
return $connection->close(4001, 'Invalid token');
}
// 可选:存入 connection 属性供后续 onMessage 使用
$connection->user_id = $payload['user_id'];
别跳过连接超时和心跳控制
Workerman 不自动管理 WebSocket 连接生命周期,如果客户端连上后不发任何数据(包括 token),这个连接会一直占着资源。必须手动加超时机制。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
推荐做法:
- 在
onWebSocketConnect中启动一个一次性定时器,比如 5 秒后检查$connection->user_id是否已设置,未设置则close() - 用
Timer::add()启动心跳检测(如每 30 秒发 ping),客户端无响应则断开 - 避免在定时器闭包里直接用
$connection变量,应改用$connection->id查找连接,防止变量引用失效
典型超时写法:
Timer::add(5, function () use ($connection) {
if (!isset($connection->user_id)) {
$connection->close('auth timeout');
}
}, null, false);
Spring Boot 用户注意:Workerman 没有 HandshakeInterceptor
如果你熟悉 Spring WebSocket 的 HandshakeInterceptor,别指望 Workerman 有类似机制。Workerman 是纯 PHP 实现,所有逻辑都得自己写进 onWebSocketConnect。Header 鉴权(如读 X-Auth-Token)理论上可行,但前提是你的前端能通过 Nginx 或代理注入 header——而浏览器原生 WebSocket 不支持,所以该路径在多数 Web 场景下不可用。
真实约束:
- 纯浏览器客户端 → 只能靠 URL 参数
- App 或桌面客户端 → 可走 header,但需确保网络链路可控(如内网直连、Nginx proxy_set_header)
- 无论哪种方式,HTTPS 是硬性前提,HTTP 下传 token 等于明文广播
最容易被忽略的一点:Workerman 的 $_GET 是在握手请求时解析的,它不是全局 HTTP 请求上下文,而是仅对当前握手有效;不要试图在 onMessage 里再读 $_GET,它为空。









