Nodejs express+socket.io,如何限制一个用户只能登录一次,一个用户只有一个连接到socket.io

Nodejs express+socket.io,如何限制一个用户只能登录一次,一个用户只有一个连接到socket.io

服务端代码,有一个问题 handshakeData.username = username; 这个好像不起作用

var connectedUsers = [];

io.set('authorization', function (handshakeData, callback) {
  var signedCookies = require('express/node_modules/cookie').parse(handshakeData.headers.cookie);
  handshakeData.cookies = require('express/node_modules/connect/lib/utils').parseSignedCookies(signedCookies, settings.cookie_secret);
  sessionStore.get(handshakeData.cookies['connect.sid'], function(err, session) {
    if (err) {
      callback(err, false);
      return;
    }
    var username = session.user && session.user.name;
    handshakeData.username = username;	//这个好像不起作用
    console.log('authorization', username);
    console.dir(connectedUsers);
    if (connectedUsers.indexOf(username) !== -1) {
      //重复登录
      console.log('重复登录');
      callback('重复登录', false);
    } else {
      connectedUsers.push(username);
      callback(null, true);
    }
  });
  //callback(null, false);
});

io.of('/client').on('connection', function (socket) {
  socket.emit('agents', agents);
  socket.emit('rate', rate);
  //socket.handshake.username
  console.log('client connect:', socket.client.request.username);
  //client下线
  socket.on('disconnect', function() {
    console.log('disconnect', socket.client.request.username);//socket.handshake.username);
    connectedUsers.splice(connectedUsers.indexOf(socket.client.request.username), 1);
	});
});

客户端代码

var client = io.connect('/client');
// 下面代码参考官方文档,但没有收到连接失败端消息,请大家指正
client.on('connect_failed', function (reason) {
    console.log('unable to connect to namespace', reason);
});

4 回复

Node.js Express + Socket.io: 如何限制一个用户只能登录一次,一个用户只有一个连接到Socket.io

服务端代码

在服务端,我们需要确保每个用户只能有一个活动的Socket连接。这可以通过使用socket.io的授权钩子(authorization hook)来实现。我们还需要维护一个用户列表,以跟踪哪些用户已经连接。

以下是修改后的服务端代码:

const io = require('socket.io')(server); // 假设 server 是你的 HTTP 服务器实例
const sessionStore = require('./sessionStore'); // 假设这是你的会话存储模块
const cookie = require('cookie');
const utils = require('express/node_modules/connect/lib/utils');

let connectedUsers = new Set(); // 使用 Set 来存储用户名

io.set('authorization', function (handshakeData, callback) {
  const cookies = cookie.parse(handshakeData.headers.cookie);
  const signedCookies = utils.parseSignedCookies(cookies, settings.cookie_secret);
  
  sessionStore.get(signedCookies['connect.sid'], function(err, session) {
    if (err) {
      callback(err, false);
      return;
    }
    
    const username = session.user && session.user.name;
    console.log('Authorization for user:', username);

    if (!username) {
      callback('未找到用户名', false);
      return;
    }

    if (connectedUsers.has(username)) {
      // 用户已连接
      callback('重复登录', false);
    } else {
      connectedUsers.add(username);
      handshakeData.username = username; // 设置 handshakeData 中的用户名
      callback(null, true);
    }
  });
});

io.of('/client').on('connection', function (socket) {
  const username = socket.handshake.query.username; // 获取用户名
  console.log(`User ${username} connected`);

  socket.on('disconnect', function () {
    console.log(`User ${username} disconnected`);
    connectedUsers.delete(username);
  });

  socket.on('someEvent', function (data) {
    console.log(`Received data from ${username}:`, data);
  });
});

客户端代码

客户端需要在连接时传递用户名,并处理连接失败的情况:

const username = 'your-username'; // 替换为实际的用户名
const client = io.connect('/client', { query: `username=${encodeURIComponent(username)}` });

client.on('connect', function () {
  console.log(`Connected as ${username}`);
});

client.on('connect_error', function (error) {
  console.error(`Connection error: ${error.message}`);
});

client.on('disconnect', function () {
  console.log(`Disconnected as ${username}`);
});

解释

  1. 服务端

    • authorization钩子中,我们从请求头中解析出Cookie,并通过会话存储获取用户的会话数据。
    • 如果用户已经存在于connectedUsers集合中,则拒绝新的连接。
    • 将用户名添加到connectedUsers集合中,并将其设置为handshakeData.username,以便在后续事件处理中使用。
  2. 客户端

    • 连接时传递用户名作为查询参数。
    • 监听connect_error事件来处理连接失败的情况。

这样,每个用户将只能有一个活跃的Socket连接。


请问你解决这个问题了吗? 我现在遇到session问题了,sokcet.io中的session好像与http中获取的req.session不一致

利用session来记录登录用户 已经登录的用户不让登录就行了 不过问题是socket.io中如何获取session啊 我和你遇到同样的问题了 不过我解决了重复登录问题 但是还没解决在socket.io中获取session的问题 望交流下 速回复我

为了实现一个用户只能登录一次,并且一个用户只有一个连接到 socket.io 的功能,我们需要改进服务端的逻辑。具体来说,我们需要确保每次新的连接都检查当前已有的用户名列表,如果用户名已经存在,则拒绝新的连接。

以下是一些改进建议和示例代码:

改进后的服务端代码

const express = require('express');
const http = require('http');
const socketIo = require('socket.io');

const app = express();
const server = http.createServer(app);
const io = socketIo(server);

let connectedUsers = new Map(); // 使用Map来存储已连接的用户,以避免重复检查

io.use((socket, next) => {
  const { headers: { cookie } } = socket.request;
  const cookies = require('cookie').parse(cookie);
  const sessionID = cookies['connect.sid'];
  
  sessionStore.get(sessionID, (err, session) => {
    if (err || !session.user || !session.user.name) return next(new Error('Invalid session'));
    
    const username = session.user.name;
    socket.handshake.username = username;

    if (connectedUsers.has(username)) {
      return next(new Error('Duplicate login'));
    }

    connectedUsers.set(username, socket.id);
    next();
  });
});

io.of('/client').on('connection', (socket) => {
  const username = socket.handshake.username;
  console.log('User connected:', username);

  socket.on('disconnect', () => {
    console.log('User disconnected:', username);
    connectedUsers.delete(username);
  });

  socket.on('reconnect_attempt', () => {
    socket.disconnect(true); // 如果用户尝试重新连接,断开连接
  });
});

server.listen(3000, () => {
  console.log('Server is running on port 3000');
});

客户端代码

客户端代码不需要做太大改动,只需处理连接失败的情况:

const client = io.connect('/client', {
  transports: ['websocket'],
  rejectUnauthorized: false
});

client.on('connect_error', (error) => {
  console.error('Connection error:', error.message);
});

client.on('connect', () => {
  console.log('Connected to server');
});

client.on('disconnect', () => {
  console.log('Disconnected from server');
});

解释

  1. 使用Map存储用户:我们使用 Map 而不是数组来存储用户名和对应的socket ID,这可以更高效地进行查找和删除操作。
  2. 验证会话:在连接时验证用户的会话信息,确保用户身份有效。
  3. 检查重复登录:在每次连接时检查 connectedUsers 中是否已经存在该用户名,如果存在则拒绝连接。
  4. 处理断开事件:当用户断开连接时,从 connectedUsers 中删除相应的记录。
  5. 客户端处理错误:客户端通过监听 connect_error 来处理连接失败的情况。

以上代码应该能满足你的需求。

回到顶部