Skip to content

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 --save

3.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 默认加上系统前缀。