2

我正在尝试将所有连接的客户端保存在一个对象中。但正如我所想,连接的客户端都有相同的 id。因此,如果对象connections具有所有连接的套接字,当我尝试向它发送消息时,connections['mac']它只会出现在我的屏幕上,就像我发送给我的一样。这是授权代码:

app.post('/auth', function(req, res){   // routes.auth(hash, db, io, pseudoArray, connections)
    var username = req.body.username,
        password = req.body.password;
    if (username != "" && password != ""){
        authenticate(username, password, db, hash, function(err, user){
            if (user) {
                // Regenerate session when signing in
                // to prevent fixation 
                console.log('user authenticated');
                req.session.regenerate(function(){
                    req.session.user = user.name;
                    req.session.success = 'Authenticated as ' + user.name
                    + ' click to <a href="/logout">logout</a>. '
                    + ' You may now access <a href="/restricted">/restricted</a>.';

                    io.sockets.on('connection', function (socket) {
                        socket.set('pseudo', req.session.user, function(){
                            pseudoArray.push(req.session.user);
                            var sessionuser = req.session.user;
                            socket.emit('pseudoStatus', 'ok');
                            connections[req.session.user] = socket;
                            console.log("user " + req.session.user + " connected");
                        });
                    });
                    res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true });
                    res.redirect('home');
                });
            } else {
                console.log('auth failed');
                req.session.error = 'Authentication failed, please check your '
                    + ' username and password.'
                    + ' (use "tj" and "foobar")';
                res.redirect('login');
            }
        }); 
    } else {
        res.redirect('connect');
    }
});

我将消息发送给其他客户端的代码:

exports.addfriend = function(connections, io){
    return function(req, res){
        // var collection = db.get('go1');
        var username = req.session.user;
        var friend = req.body.name;
        console.log(connections[''+friend+'']);
        connections[''+friend+''].emit('addRequest', username);
        return;
    }
}

我之前在路由文件中有这个身份验证代码,我想我应该在 app.js 上运行它。我这样做了,但它仍然无法按我的意愿工作。有人能告诉我我错过了什么吗?

4

2 回答 2

5

您的代码的问题是您的一个 HTTP 路由中有一个 Socket.IO 传入连接处理程序。这意味着处理程序仅在访问路由后应用,除了您应该只有一个 Socket.IO 连接处理程序的事实。

您需要做的是将 HTTP 和 Socket.IO 处理程序分开,并且由于您使用会话,因此允许 Socket.IO 处理授权。

首先,将您的套接字处理程序移到 HTTP 处理程序之外:

app.post('/', handler);
io.sockets.on('connection', handler);

然后,定义 Socket.IO 授权设置以从 Express 中获取会话对象。假设您正在使用express.cookieParser(),因为您有会话和会话存储,我们需要从外部引用它们:

var MemoryStore = express.session.MemoryStore;

var session_key = 'express.sid';
var session_secret = 'for signed cookies';
var session_store = new MemoryStore();

var cookieParser = express.cookieParser(session_secret);

app.use(cookieParser);
app.use(express.session({
  secret: session_secret,
  store: session_store,
  key: session_key
});

现在可以访问会话对象和 cookie 解析器,我们可以定义授权设置:

io.set('authorization', function(handshake, callback) {
  if (handshake.headers.cookie) {
    cookieParser(handshake, null, function(err) {
      // Use depends on whether you have signed cookies
      // handshake.sessionID = handshake.cookies[session_key];
      handshake.sessionID = handshake.signedCookies[session_key];

      session_store.get(handshake.sessionID, function(err, session) {
        if (err || !session) {
          callback('Error or no session.', false);
        } else {
          handshake.session = session;
          callback(null, true);
        }
      });
    });
  } else {
    callback('No session cookie found.', false);
  }
});

这段代码的作用是检查客户端是否有会话 cookie。如果不是,它不会授权 Socket.IO 连接。如果是,它会解析 cookie,找到关联的会话,并将会话与套接字一起存储。现在您可以通过套接字访问会话属性:

app.post('/', function(req, res) {
  req.session.user = 'genericusername';
  res.send(200);
});

io.sockets.on('connection', function(socket) {
  var session = socket.handshake.session;
  session.user // genericusername
});

至于您的代码,它将如下所示:

var http = require('http');
var express = require('express');
var app = express();

var server = http.createServer(app);
var io = require('socket.io').listen(server);

var MemoryStore = express.session.MemoryStore;

var session_key = 'express.sid';
var session_secret = 'for signed cookies';
var session_store = new MemoryStore();

var cookieParser = express.cookieParser(session_secret);

app.use(cookieParser);
app.use(express.session({
  secret: session_secret,
  store: session_store,
  key: session_key
});

路由处理程序:

app.post('/auth', function(req, res) {
  var username = req.body.username,
  var password = req.body.password;

  if (username != '' && password != '') {
    authenticate(username, password, db, hash, function(err, user) {
      if (user) {
        req.session.regenerate(function() {
          req.session.user = user.name;
          req.session.success = 'Authenticated as ' + user.name + ' click to <a href="/logout">logout</a>. You may now access <a href="/restricted">/restricted</a>.';
          res.cookie('rememberme', '1', {
            maxAge: 900000,
            httpOnly: true
          });
          res.redirect('/home');
        });
      } else {
        req.session.error = 'Authentication failed, please check your username and password. (use "tj" and "foobar")';
        res.redirect('/login');
      }
    }); 
  } else {
    res.redirect('/connect');
  }
});

然后是 Socket.IO 配置:

io.set('authorization', function(handshake, callback) {
  if (handshake.headers.cookie) {
    cookieParser(handshake, null, function(err) {
      // Use depends on whether you have signed cookies
      // handshake.sessionID = handshake.cookies[session_key];
      handshake.sessionID = handshake.signedCookies[session_key];

      session_store.get(handshake.sessionID, function(err, session) {
        if (err || !session) {
          callback('Error or no session.', false);
        } else {
          handshake.session = session;
          callback(null, true);
        }
      });
    });
  } else {
    callback('No session cookie found.', false);
  }
});

io.sockets.on('connection', function(socket) {
  var session = socket.handshake.sessionl
  socket.set('pseudo', session.user, function() {
    socket.emit('pseudoStatus', 'ok');
    connections[session.user] = socket;
  });
});
于 2013-10-14T15:33:21.780 回答
1

你有一个io.sockets.on('connection'...内部的单个用户登录。所以每次用户登录时,都会添加另一个监听器。

所以它可能看起来像这样(有人登录,然后他们连接到套接字)

User1 登录 User 1 连接到套接字(伪自己)

用户 2 登录用户 2 连接到套接字(伪用户 1,然后伪用户 2)

用户 3 登录用户 3 连接到套接字(伪为 1、2,然后是 3

如果用户 1 刷新页面,他也将伪为 1、2、3,以及

问题是每次有人登录时听得越来越多。

您需要在多次发生的函数之外进行连接。

我通常会执行以下操作

用英语:用户加载一个页面,他连接到套接字 io,然后他执行 ajax $.get 以便服务器可以使用他的会话给他一个令牌,该令牌将授权他的套接字。然后他发送令牌,服务器知道套接字已被授权。

app.get('/socket_token', function(req, res, next){
  var socket_token = Math.random();/* probably want to use crypto here instead*/
  mysql.query('update users set users.socket_token = ? where users.id = ?', [  socket_token, req.session.userId], function(err, result){
    if(result)
      res.json({userId: req.session.userId, socket_token: socket_token});
    else
      res.send('DOH!');
   });
});

global.all_my_sockets = []

io.sockets.on('connnection', function(socket){

  socket.emit('hey_you_are_connected');

  socket.on('authorize', function(credentials, callback){
    if(credentials.userId)
      mysql.query('select * from users where id = ?', [credentials.userId], function(err, user){
        if(user.socket_token == credentials.socket_token){
          socket.set('authorized', true);
          socket.set('userId', user.id);

          // This is probably bad since what if he has 2 browser windows open?
          all_my_sockets[user.id] = socket

          callback('that socket token is authorized');
        } else {
         //handle it
         callback('that socket token is no good');
        }
      })

  });
  socket.on('disconnect', function(){
    socket.get('userId', function(id){
      if(id)
        delete all_my_sockets[id];
    });
  });
})

在客户端

socket = io.connect();
socket.on('hey_you_are_connected', function(){
  $.get('/socket_token', function(credentials){
    socket.emit('authorize_token', credentials, function(res){
      console.log(res);  // did we authorize socket?
    });
  });
});

我在脑海中输入了这个,所以可能会有错误,但它应该非常接近你想要做的事情。

所以在 all_my_sockets[id] 中保存套接字存在一些问题,即每个用户只能有 1 个套接字,如果他们打开了多个浏览器怎么办?

您应该尝试以不同的方式构造您的代码,这样您就不需要全局的一堆套接字,否则,只需将它们推送到数组中,这样每个用户就可以存在多个。

all_my_sockets.push(socket);
...
socket.on('disconnect', function(){
  if( all_my_sockets.indexOf(socket) != -1 )
    all_my_sockets.splice( all_my_sockets.indexOf(socket), 1 )
})

如果您在授权之前将每个套接字推到阵列中,那么要真正找到套接字,您将不得不遍历每个:

_.each(all_my_sockets, function(socket){
  socket.get('userId', function(userId) { 
    if(userId)
      doSomething(socket)
    else
      doSomethingUnAuthorized(socket)
   });
 });

显然这非常慢,这也是为什么没有很多人将套接字保存到像这样的全球性地方的原因。尝试使用更多回调来做你想做的事情,并从头开始重新考虑问题。或者将其保留为 hack,直到您获得更多流量并且它成为性能问题。:)

于 2013-10-14T14:54:49.360 回答