使用 Express.js v4 和 Socket.io v1 的会话

Posted

技术标签:

【中文标题】使用 Express.js v4 和 Socket.io v1 的会话【英文标题】:Using sessions with Express.js v4 and Socket.io v1 【发布时间】:2014-09-26 09:47:17 【问题描述】:

如何在 express.js 中保存会话数据并在 socket.io 事件中访问它?

我正在使用 express.js v4socket.io v1 和基本的 express-session 中间件开发一个 webapp。

我花了几个小时试图弄清楚这一点,但 Stack Overflow 上的所有当前答案仅适用于 express v3 和 socket.io v0.9。不幸的是,我不能使用 express.io,因为它只是一个使用这些旧版本的包装器。

我目前的解决方案是彻底破解:

app.get('/auth', function(req, res) 
    if(verified(req.query)) 
        authed[req.sessionID] = true;
    
);

io.sockets.on('connection', function(websocket) 
    var cookies = cookie.parse(websocket.handshake.headers.cookie);
    var decrypted = cookieParser.signedCookies(cookies, random);
    var sessionID = decrypted['connect.sid'];

    websocket.on('input', function(input) 
        if(authed[sessionID]) 
            // Do stuff...
        
    );
);

我将我的“会话数据”作为基于会话 ID 的键/值存储保存在对象中。这适用于单进程应用程序,但它确实不是一个好的解决方案。它实际上并没有让我访问保存在 req.session 中的任何内容,如果进程结束,我保存的所有数据都将丢失。

最重要的是,我需要加载第 3 方 cookie 包来解析来自 socket.io 的 cookie 字符串。 cookie-parser 的新版本似乎取消了从字符串中解析 cookie 的功能。至少,它没有记录在案。

一定有更好的方法!

编辑:最后决定用express redis,在socket.io中写了一个函数,根据session ID从redis中获取信息。

【问题讨论】:

你能检查我的问题吗? superuser.com/questions/985825/… 【参考方案1】:

您可以将会话存储在 Memcached 或 Redis 中。然后,您可以从其中任一商店获取会话数据。

memcache 和 redis 包都可用于 nodejs。

还请注意,您可以在 socket.io 中使用中间件进行身份验证。然后您不必将身份验证逻辑放入您的连接事件处理程序中。

var authorization = require('./socket/authorization')(app);
io.use(authorization.authorize);

作为示例,这个 memcached auth 内容在我们的例子中是读取我们的 php 存储的 cookie。

var memcached = require('memcached'),
    cookie = require('cookie),
    debug = require('debug')('socket.io:authorization');

module.exports = function(app) 
    var authorize = function(socket, next) 
        var handshakeData = socket.request;
        if (handshakeData.headers.cookie) 
            handshakeData.cookie = cookie.parse(handshakeData.headers.cookie);
            if (typeof handshakeData.cookie['node'] === 'undefined') 
                next(new Error('No cookie transmitted.'));
             else 
                var loginEndIndex = handshakeData.cookie['node'].indexOf(',');
                handshakeData.node = handshakeData.cookie['node'].slice(0, loginEndIndex);

                var memcached = new Memcached(app.config.memcached.server, app.config.memcached.options);
                memcached.on('failure', function(details) 
                    debug('Server: %s went down due to %s', details.server, details.messages.join(' '));
                );
                memcached.on('reconnecting', function(details) 
                    debug('Total downtime caused by server %s: %sms', details.server, details.totalDownTime);
                );

                memcached.get(handshakeData.cookie['PHPSESSID'], function(err, result) 
                    if (!err && result !== false) 
                        var pipeIndex = result.indexOf('|'),
                            phpData = result.slice(pipeIndex + 1),
                            obj = php.unserialize(phpData);

                        if (handshakeData.node === obj.userLogin) 
                            debug('coockie-accepted: %s, %s', handshakeData.node, obj.userLogin);
                            next();
                         else 
                            debug('cookie-revoked; %s, %s', handshakeData.node, obj.userLogin);
                            next(new Error('Cookie is invalid.'));
                        
                     else 
                        debug('error: %s', err);
                        next(new Error('Cookie is invalid.'));
                    
                    memcached.end();
                );
            
         else 
            debug('error: No cookie transmitted.');
            next(new Error('No cookie transmitted.'));
        
    ;

    return 
        authorize: authorize
    ;
;

【讨论】:

【参考方案2】:

我在使用 express.js 4.0 和 socket.io 0.9 时遇到了类似的问题。为了解决我的问题,我用自己的函数设置了“授权”过程,这是对我在网上找到的一些解决方案的修改。在此函数中,您将会话添加到数据属性并从套接字的握手属性访问它。看看代码。

我在 sockets 文件夹中创建了 index.js 和这个内容:

sio.set('authorization', function(data, accept)
  /* NOTE: To detect which session this socket is associated with,
   *       we need to parse the cookies. */
  if (!data.headers.cookie) 
    return accept(null, true);
  

  data.cookie = cookie.parse(data.headers.cookie);
  data.cookie = parse.signedCookies(data.cookie, sessionContainer.secret);
  data.sessionID = data.cookie[sessionContainer.key];
  sessionContainer.store.get(data.sessionID, function(err, session)
    if (err) 
      return accept('Error in session store.', false);
     else if (!session) 
      return accept('Session not found.', false);
    
    if(!session.username)
      return accept('Session not authenticated', true);
    
    data.session = session;
    return accept(null, true);
  );
);

然后使用session中存储的数据:

sio.sockets.on('connection', function (socket) 
  var hs = socket.handshake;
  /* NOTE: At this point, you win. You can use hs.sessionID and
   *       hs.session. */
  if(hs.sessionID)
    // logged in user
    console.log('A socket with sessionID '+hs.sessionID+' connected.');
    // here you can access whatever you stored in the session
    console.log('User  ' + hs.session.username);
    socket.emit('news',  hello: 'world' );
    socket.on('my other event', function (data) 
      console.log(data);
    );
    clientSockets[hs.sessionID] = socket;
    // handle the disconnect
    socket.on('disconnect', function () 
      if(clientSockets[hs.sessionID])
        console.log('deleted unused socket ' + hs.sessionID);
        delete clientSockets[hs.sessionID];
      
    );
  
);

您可以在以下链接中找到整个项目 https://github.com/landreus/dimiblog/blob/master/sockets/index.js

希望对你有帮助!

【讨论】:

以上是关于使用 Express.js v4 和 Socket.io v1 的会话的主要内容,如果未能解决你的问题,请参考以下文章

使用 express.js 和 socket.io 构建推送通知系统的最佳方法

没有 express.js 的 Socket.io 会话?

在 Express.js 项目中使用 Socket.io

javascript Express.js和Socket.io使用相同的端口

Express.js 'socket.io/socket.io.js 404'

如何结合 Express.JS 使用 Socket.io(使用 Express 应用程序生成器)