You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Socket.IO中间件中如何获取连接套接字?实现单用户单连接

问题解决:Socket.io 拦截同一用户多连接及中间件访问IO实例方法

问题背景

通过Socket.io中间件验证JWT令牌,将用户信息挂载到socket.user,但需要判断同一用户是否通过不同套接字重复连接,此时中间件无法直接获取io实例来查询已连接用户列表。

方法一:在中间件中访问IO实例

Socket.io的socket对象本身自带io属性,可直接通过socket.io获取全局IO实例,无需额外传递。修改中间件添加重复连接校验逻辑:

const protectSocket = async (socket, next) => {
  const token = socket.handshake.auth.token;
  if (token) {
    try {
      const decoded = jwt.verify(token, process.env.JWT_SECRET);
      // 获取用户信息
      const user = await User.findById(decoded.id).select("-password");
      
      // 检查当前用户是否已有活跃连接
      const connectedSockets = Array.from(socket.io.sockets.sockets.values());
      const hasExistingConnection = connectedSockets.some(s => 
        s.user && s.user._id.toString() === user._id.toString()
      );

      if (hasExistingConnection) {
        return next(new Error("用户已存在活跃连接"));
      }

      socket.user = user;
      return next();
    } catch (err) {
      console.log(err);
      return next(new Error(err.message || "令牌验证失败"));
    }
  } else {
    return next(new Error("Token is missing"));
  }
};

也可通过闭包在注册中间件时传递IO实例,适合自定义参数场景:

// 注册中间件时传入io
io.use((socket, next) => protectSocketWithIO(socket, next, io));

// 定义带io参数的中间件函数
const protectSocketWithIO = async (socket, next, io) => {
  // 直接使用io实例查询已连接套接字
  const connectedSockets = Array.from(io.sockets.sockets.values());
  // ... 其余校验逻辑同上
};

方法二:不通过中间件实现重复连接拦截

直接在connection事件中处理校验和拦截,此时可直接获取io实例:

io.on("connection", async (socket) => {
  const token = socket.handshake.auth.token;
  if (!token) {
    socket.disconnect(true);
    return;
  }

  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    const user = await User.findById(decoded.id).select("-password");
    
    // 检查重复连接
    const hasExistingConnection = Array.from(io.sockets.sockets.values()).some(s => 
      s.user && s.user._id.toString() === user._id.toString()
    );

    if (hasExistingConnection) {
      socket.disconnect(true);
      return;
    }

    socket.user = user;
    // 后续业务逻辑处理
  } catch (err) {
    socket.disconnect(true);
    console.log(err);
  }
});

注意事项

  • 中间件方式更适合通用权限校验,代码解耦性强;connection事件处理更直接,无需考虑实例传递问题。
  • 若使用MongoDB,注意将_id转为字符串后再比较,避免ObjectId类型不一致导致判断错误。

内容的提问来源于stack exchange,提问作者Hammad Ali

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 21:24:26