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
相关产品推荐
相关产品推荐

