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}`);
});
解释
-
服务端:
- 在
authorization
钩子中,我们从请求头中解析出Cookie,并通过会话存储获取用户的会话数据。 - 如果用户已经存在于
connectedUsers
集合中,则拒绝新的连接。 - 将用户名添加到
connectedUsers
集合中,并将其设置为handshakeData.username
,以便在后续事件处理中使用。
- 在
-
客户端:
- 连接时传递用户名作为查询参数。
- 监听
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');
});
解释
- 使用Map存储用户:我们使用
Map
而不是数组来存储用户名和对应的socket ID,这可以更高效地进行查找和删除操作。 - 验证会话:在连接时验证用户的会话信息,确保用户身份有效。
- 检查重复登录:在每次连接时检查
connectedUsers
中是否已经存在该用户名,如果存在则拒绝连接。 - 处理断开事件:当用户断开连接时,从
connectedUsers
中删除相应的记录。 - 客户端处理错误:客户端通过监听
connect_error
来处理连接失败的情况。
以上代码应该能满足你的需求。