You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何向已认证用户的WebSocket传递信息以追踪在线用户

解决WebSocket已认证客户端的用户追踪问题

我之前也碰到过类似的场景——登录认证后建立WebSocket,想给每个连接绑定用户名并维护在线列表,折腾了好一阵才搞定。结合你的情况,我整理了几个关键步骤和代码示例,应该能帮你解决问题:

核心思路

问题的关键在于在WebSocket握手阶段把已认证的用户信息传递给服务器,然后在服务器端将用户与对应的Socket实例关联起来,最后维护一个在线用户的存储结构来追踪状态。


客户端修改:认证后携带用户信息建立WebSocket

你的authenticate函数成功后,建立WebSocket连接时,需要把认证后的用户名(或更安全的token)通过查询参数传递给服务器(因为自定义请求头在部分浏览器的WebSocket握手里可能不被支持)。

示例代码:

async function authenticate(username, password) {
  // 登录请求逻辑,假设返回包含用户名的成功响应
  const loginRes = await fetch('/api/login', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ username, password })
  });

  const loginData = await loginRes.json();
  if (!loginData.success) throw new Error('登录失败');

  // 认证成功后,携带用户名建立WebSocket连接(注意encodeURIComponent避免特殊字符问题)
  const ws = new WebSocket(`ws://your-server-domain/ws?username=${encodeURIComponent(loginData.username)}`);

  // 可选:监听WebSocket连接状态
  ws.onopen = () => console.log(`已以${loginData.username}身份建立WebSocket连接`);
  ws.onerror = (err) => console.error('WebSocket连接出错:', err);

  return ws;
}

服务器端处理:关联用户与Socket并维护在线列表

以Node.js的ws库为例(其他语言/框架逻辑类似),服务器需要在WebSocket连接建立时,从请求URL中提取用户信息,关联到Socket实例,并维护一个在线用户集合。

基础版(直接传递用户名)

const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });

// 用Map存储在线用户:key为用户名,value为对应的Socket实例
const onlineUsers = new Map();

wss.on('connection', (ws, req) => {
  // 从请求URL的查询参数中提取用户名
  const urlParams = new URLSearchParams(req.url.slice(1)); // 去掉URL开头的"/"
  const username = urlParams.get('username');

  // 如果没有获取到用户名,直接拒绝连接
  if (!username) {
    ws.close(401, '未提供有效认证信息');
    return;
  }

  // 将用户名绑定到当前Socket实例
  ws.username = username;
  // 添加到在线用户列表
  onlineUsers.set(username, ws);

  console.log(`${username} 已上线,当前在线用户:${Array.from(onlineUsers.keys()).join(', ')}`);

  // 监听Socket关闭事件,从在线列表中移除用户
  ws.on('close', () => {
    onlineUsers.delete(username);
    console.log(`${username} 已下线,当前在线用户:${Array.from(onlineUsers.keys()).join(', ')}`);
  });

  // 处理客户端消息(可选)
  ws.on('message', (data) => {
    console.log(`收到${username}的消息:${data.toString()}`);
  });
});

更安全的版本(用JWT Token)

如果担心用户名被篡改,可以在登录时返回JWT Token,客户端携带Token建立WebSocket,服务器验证Token后解析出用户名:

const WebSocket = require('ws');
const jwt = require('jsonwebtoken');
const wss = new WebSocket.Server({ port: 8080 });

const onlineUsers = new Map();
const JWT_SECRET = 'your-secure-secret-key'; // 建议用环境变量存储

wss.on('connection', (ws, req) => {
  const urlParams = new URLSearchParams(req.url.slice(1));
  const token = urlParams.get('token');

  if (!token) {
    ws.close(401, '未提供认证Token');
    return;
  }

  try {
    // 验证并解析Token
    const decoded = jwt.verify(token, JWT_SECRET);
    const username = decoded.username;

    ws.username = username;
    onlineUsers.set(username, ws);

    console.log(`${username} 已通过Token认证并上线`);
  } catch (err) {
    // Token无效,拒绝连接
    ws.close(403, '无效的认证Token');
    return;
  }

  // 后续的close、message监听逻辑和基础版一致
});

常见踩坑点排查

如果还是没成功,可以检查这几个地方:

  • 客户端建立WebSocket时,是否正确传递了用户信息?可以在浏览器控制台查看WebSocket的请求URL确认。
  • 服务器是否正确解析了查询参数?比如req.url的格式是否符合预期,有没有漏掉处理开头的"/"。
  • 在线用户存储是否用了合适的结构?用Map比普通对象更稳妥,避免用户名作为键名的冲突问题。
  • 是否处理了Socket关闭事件?如果没在close事件中移除用户,会导致在线列表出现僵尸数据。

内容的提问来源于stack exchange,提问作者Jeffrey penner

火山引擎 最新活动