WebSocket 接口鉴权与客户端接入指南
当客户端需要监听敏感数据通道(如单个设备的遥测状态 private-device.{device_id})时,系统要求强制启用基于私有通道的签名验证机制。本文档定义了该鉴权协议的后端签名算法和前端接入的完整实现。
1. 签名鉴权机制原理
私有通道及存在通道在建立连接前必须通过后端代理签名校验。其标准认证交互时序如下:
mermaid
sequenceDiagram
participant Web as Web 客户端
participant Backend as 业务后端 (HTTP API)
participant WSS as WebSocket Server
Web->>WSS: 1. 建立基础 WS 连接 (免鉴权)
WSS-->>Web: 2. 握手成功,分发临时连接 ID (socket_id, 如 "1234.5678")
Web->>Backend: 3. 发送授权请求 (携带 JWT Token、socket_id 和 channel_name)
Backend->>Backend: 4. 验证用户 JWT,并计算 HMAC-SHA256 签名 (auth_signature)
Backend-->>Web: 5. 授权成功,返回 signature JSON
Web->>WSS: 6. 发送订阅协议包 (携带 channel_name 和 signature)
WSS->>WSS: 7. 用同算法校验签名,判定是否具备通道权限
WSS-->>Web: 8. 订阅建立成功 (开始接收该通道的实时推送)2. 后端签名算法规范 (Authentication Endpoint)
当客户端向业务后端的 /v1/broadcasting/auth 接口请求授权时,需要提交:
请求体:
json{ "socket_id": "1234.5678", "channel_name": "private-device.dev_f49b10a2" }后端计算公式: 后端在验证用户身份(读取请求头中的 JWT 发现该用户确实拥有该设备后),使用应用的
Secret计算签名值: $$\text{auth_signature} = \text{HMAC-SHA256}(\text{app_secret}, \text{socket_id} + ":" + \text{channel_name})$$响应体: 返回符合 Pusher 规范的加密鉴权结果:
json{ "auth": "app_key:5db72005a39cb2e0b57ea3d969... (计算得出的 auth_signature 字符串)" }
3. 客户端前端接入开发 (Echo / Pusher SDK)
前端需要使用 pusher-js 和 @holloway/laravel-echo(或官方 laravel-echo)库来实现自动握手。
3.1 核心依赖安装
bash
npm install pusher-js laravel-echo --save3.2 完整集成与订阅代码
创建一个 websocket-listener.js 服务文件,负责初始化连接和私有通道订阅:
javascript
import Echo from 'laravel-echo';
import Pusher from 'pusher-js';
// 将 Pusher 挂载到 window 对象上供 Echo 内部使用
window.Pusher = Pusher;
// 1. 初始化 Echo 客户端
const jwtToken = localStorage.getItem('access_token');
const echo = new Echo({
broadcaster: 'reverb', // 指定 Reverb 推送引擎
key: 'your-reverb-app-key',
wsHost: 'ws.example.com',
wsPort: 443,
wssPort: 443,
forceTLS: true,
disableStats: true,
// 必须:配置后端的授权端点
authEndpoint: 'https://api.example.com/v1/broadcasting/auth',
auth: {
headers: {
// 携带 JWT 身份令牌,以便业务后台识别当前用户的身份权限
Authorization: `Bearer ${jwtToken}`,
Accept: 'application/json'
}
}
});
const DEVICE_ID = 'dev_f49b10a2';
// 2. 订阅私有通道,并监听特定的实时事件
const deviceChannel = echo.private(`device.${DEVICE_ID}`);
deviceChannel
// 监听遥测值实时波动更新事件
.listen('.TelemetryUpdated', (data) => {
console.log('[WSS] 收到实时遥测数据:', data.metrics);
updateTelemetryUI(data.metrics);
})
// 监听设备离线或异常告警事件
.listen('.DeviceAlarm', (data) => {
console.warn(`[WSS] 警告!检测到设备告警! 级别: ${data.level}`);
showAlarmToast(data);
});
// 监听连接状态以做 UI 提示
echo.connector.pusher.connection.bind('state_change', (states) => {
console.log(`[WSS] 连接状态从 ${states.previous} 变更为 ${states.current}`);
});
function updateTelemetryUI(metrics) {
// 具体的数据渲染逻辑,如改变图表数据
const tempElement = document.getElementById('temp-value');
if (tempElement && metrics.temperature) {
tempElement.innerText = `${metrics.temperature} °C`;
}
}
function showAlarmToast(alarm) {
// 前端通知弹窗
alert(`【紧急告警】设备 ${alarm.deviceId}: ${alarm.message}`);
}提示:注意事件监听名称前方的句点 .,在 Pusher 协议中,若使用自定义 Namespace 事件,首位字符需为句点(.)以避免 SDK 默认加上系统前缀。