【问题标题】:socket.io and express 4 sessionssocket.io 和 express 4 个会话
【发布时间】:2014-06-23 00:38:09
【问题描述】:

我想在我的 socket.io 应用程序中访问 express 4 会话。 我是 Node 的新手,在实现这个功能时遇到了一些麻烦。

我找到了一个允许访问 express 4 会话的 npm 模块:https://www.npmjs.org/package/session.socket.io-express4https://github.com/eiriklv/session.socket.io

如果您查看下面我的 app.js 代码,我在 sessionsessionStorecookieParser 设置中做错了,因为我无法让这个模块工作。

// init modules
var express = require('express');
var helmet = require('helmet');
var fs = require('fs');
var path = require('path');
var favicon = require('static-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var session = require('express-session');
var memoryStore = session.MemoryStore;
var app = express();

// set variables
var options = {
  key: fs.readFileSync('./openssl_keys/server_key.pem'),
  cert: fs.readFileSync('./openssl_keys/server_cert.pem')
};
var cookieSecret = "secret phrase";
var sessionStore = new memoryStore();

app.set('env', process.env.NODE_ENV || 'development');

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

app.use(favicon());
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded());
app.use(cookieParser(cookieSecret));
app.use(session({
    secret: cookieSecret,
    cookie: {httpOnly: true, secure: true},
    store: sessionStore
}));
app.use(function(req, res, next){
    res.locals.session = req.session;
    next();

});
app.use(express.static(path.join(__dirname, 'public')));

//routes
require('./routes/index')(app);
require('./routes/test')(app);


// starting http and https servers
var http = require('http').createServer(app).listen(8000, function(){
    console.log("http server listening on port 8000");
});
var https = require('https').createServer(options, app).listen(8080, function(){
    console.log("https server listening on port 8080"); 
});

// starting socket.io & session handler
var serverIO = require('socket.io').listen(https);
var SessionSockets  = require('session.socket.io-express4');
var io = new SessionSockets(serverIO, sessionStore, cookieParser);

io.on('connection', function(err, socket, session){
    if(err) throw err;
    console.log("connected");
    //console.log(session);
    socket.on('clientMessage', function(content) {
        console.log("received client message")
        console.log(content);
    });

});

module.exports = app;

我尝试了多种可能性,例如:

  • 禁用https 服务器。
  • 使用密码短语设置cookieParser 对象(因此它“实际上”将密码短语导出到io = new SessionSockets(serverIO, sessionStore, cookieParser);
  • 使用最少的cookie 选项。

无论如何我对此有点迷茫,欢迎任何建议/批评。


更新

好的,经过多次尝试,我想我可以让它工作!

问题在于 cookieParser 初始化,正确的方法似乎是:

var cookieParser = require('cookie-parser');
app.use(cookieParser());
app.use(session({
    secret: "secret phrase",
    cookie: {httpOnly: true, secure: true},
    store: sessionStore
}));
var io = new SessionSockets(serverIO, sessionStore, cookieParser());

请注意,如果我使用 var io = new SessionSockets(serverIO, sessionStore, cookieParser);(而不是 cookieParser()),则它不起作用。 这似乎是问题所在。

如果我使用:

app.use(cookieParser("secret phrase"));
app.use(session({
    secret: "secret phrase",
    cookie: {httpOnly: true, secure: true},
    store: sessionStore
}));
var io = new SessionSockets(serverIO, sessionStore, cookieParser("secret phrase"));

然后模块崩溃并显示以下错误消息:

session.socket.io-express4/session.socket.io.js:41
ake.signedCookies[key] = handshake.signedCookies[key].match(/\:(.*)\./).pop();
                                                                        ^
TypeError: Cannot call method 'pop' of null

但如果我使用:

app.use(cookieParser("secret phrase"));
app.use(session({
    secret: "secret phrase",
    cookie: {httpOnly: true, secure: true},
    store: sessionStore
}));
var io = new SessionSockets(serverIO, sessionStore, cookieParser());

然后一切看起来都很好。

现在,在 cookie-parser 文档 (https://github.com/expressjs/cookie-parser) 中,您可以传递一个密钥来对 cookie 进行签名。这是我想要的。

有人可以解释一下 cookie-parser 密码短语和会话密码短语的关系吗?它们必须相同/不同吗?

【问题讨论】:

标签: node.js session express socket.io


【解决方案1】:

这是我针对以下环境的解决方案:

  • 快递4.2.0
  • socket.io 1.1.0
  • cookie 解析器 1.0.1
  • cookie 会话 1.0.2

代码:

var cookieParser = require('cookie-parser')();
var session = require('cookie-session')({ secret: 'secret' };

...

app.use(cookieParser);
app.use(session);

...

io.use(function(socket, next) {
    var req = socket.handshake;
    var res = {};
    cookieParser(req, res, function(err) {
        if (err) return next(err);
        session(req, res, next);
    });
});

然后你可以通过套接字的握手访问会话:

io.on('connection', function (socket) {
    console.log("Session: ", socket.handshake.session);
});

对于想知道它如何/为什么起作用的人:

  • 我们通过 cookie 解析器发送 handshake 请求,以便 cookie 可用
  • 然后我们通过会话中间件发送handshake,就好像它是一个正常的请求一样
  • 中间件将session附加到请求中
  • 我们使用handshake 是因为出于所有意图和目的,这是一个正常的请求,解析器和会话中间件可以正确处理它。这就是为什么您必须通过handshake 访问session

【讨论】:

  • @Sean Adkinson 我不知道如何写入 cookie,你能帮忙吗?如果我将数据保存到 socket.handshake.session 它不会持续存在。
  • @youbetternot 我不是 100% 确定,因为我没有尝试过,但是使用 websocket 连接,您可能无法更新会话 cookie,因为浏览器是'不会读取它接收到的 websocket 消息的 Set-Cookie 标头。可能只有在最初的握手时它才会通过正常过程。如果你使用 server-cookies,你可能会有更好的运气,因为会话信息存储在服务器上,你可以在那里更新它。有意义吗?
  • @SeanAdkinson 好的,感谢您的重播,我仍在努力寻找正确的解决方案。
【解决方案2】:

express 4.13.4 / socket.io 1.4.5

我浏览了所有解决方案和模块,但它们都不能在我的应用程序中运行。 最后——

app.use(session({

      secret: COOKIE_SECRET,
      resave: true,
      saveUninitialized: true,
      store:sessionStore,
      cookie: { domain: 'localhost',secure: false } 

}));  


io.use(function(socket, next) {
                          session({
                              secret: COOKIE_SECRET,
                              resave: true,
                              saveUninitialized: true,
                              store:sessionStore,
                              cookie: { domain: 'localhost',secure: false } 
                          })(socket.handshake, {}, next);
});

像魅力一样工作。

【讨论】:

  • ?不幸的是,它会导致 MaxListenersExceededWarning:Possible EventEmitter memory leak detected nodejs 错误。
【解决方案3】:

express-socket.io-session 是针对您的问题的现成解决方案。通常在 socket.io 端创建的会话与在 express.js 中创建的会话具有不同的 sid

在知道这个事实之前,当我通过它寻找解决方案时,我发现了一些奇怪的东西。从 express.js 实例创建的会话可以在 socket.io 端访问,但相反的情况是不可能的。很快我就知道我必须通过管理 sid 来解决这个问题。但是,已经编写了一个包来解决这个问题。它有据可查,可以完成工作。希望对你有帮助

【讨论】:

    【解决方案4】:

    使用新的 express-session 中间件,您只需添加 IO 中间件:

    io.use(function(socket, next) {
      session(socket.handshake, {}, next);
    });
    

    一个完整的例子如下所示:

    var io = require('socket.io')(server);
    
    var Session = require('express-session'),
        SessionStore = require('session-file-store')(Session);
        session = Session({
          store: new SessionStore({ path: './tmp/sessions' }),
          secret: 'pass',
          resave: true,
          saveUninitialized: true
        });
    
    io.use(function(socket, next) {
      session(socket.handshake, {}, next);
    });
    
    io.on('connection', function(socket){
      console.log('a user connected');
      socket.emit('chat message', "UserID: " + socket.handshake.session.uid);
    });
    

    我创建了一个超级迷你 npm 包socket.io-express-session,它的工作原理与我上面解释的一样。

    【讨论】:

    【解决方案5】:

    这对我有用

    • 快递4.9.0
    • express.io 1.1.13
    • 连接-redis 2.1.0
    • 快速会话 1.8.2

    我想要的是通过 redis 与前端和后端 API 共享会话。 不同的机器,共享相同的数据库。在前端打开页面时会创建会话并登录用户,然后 api 会根据请求查找登录的用户。

    var cookieParser = require('cookie-parser')();
    var session = require('express-session');
    var RedisStore = require('connect-redis')(session);
    
    var db = require('./db')(config);
    
    var sessionStore = session( {
            store: new RedisStore({ client: db }),
            secret: SECRET,
            cookie: { secure: false }
        }
    );
    
    app.use(cookieParser);
    app.use(sessionStore);
    
    // attach sessions to pure websocket requests
    app.io.use(function(req, next) {
        var res = {};
        cookieParser(req, res, function(err) {
            if (err) { return next(err); }
            sessionStore(req, res, next);
        });
    });
    

    注意:我将 cookie.secure 设置为 false,因此我可以在本地不使用 https 进行测试。

    【讨论】:

    • io.use() 接受一个套接字作为它的第一个参数。 cookieParser 和 sessionStore 的第一个参数应该是 socket.handshake。除此之外,您的解决方案似乎有效:)
    • 那是我最初找到的代码,但它并不完全有效。不知道为什么。我可以试试,然后添加进去
    【解决方案6】:

    这让我很难找到正确的解决方案。这对我有用:

    /*
    
        Just to see, before my code : 
    
        var sessionStore = new mongoStore({
            db: db.connection.db,
            collection: config.sessionCollection
        });
    
        app.use(session({
            secret: config.sessionSecret,
            store: sessionStore
        }));
    
    */
    
    io.use(function(socket, next) {
    
        var handshake = socket.handshake;
    
        if (handshake.headers.cookie) {
    
            cookieParser(config.sessionSecret)(handshake, {}, function(err) {
    
                handshake.sessionID = handshake.signedCookies['connect.sid']; // <- 'connect.sid' > your key could be different, but this is the default 
                handshake.sessionStore = sessionStore;
    
                handshake.sessionStore.get(handshake.sessionID, function(err, data) {
    
                    if (err) return next(err);
    
                    if (!data) return next(new Error('Invalid Session'));
    
                    handshake.session = new session.Session(handshake, data);
                    next();
                });
            });
    
        } else {
    
            next(new Error('Missing Cookies'));
        }
    });
    

    express 4.2.0 / socket.io 1.0.6

    【讨论】:

      【解决方案7】:

      这可能适用于 express 4 / socket.io 1.X 我抓住了这个代码表单https://github.com/turbonetix/bus.io/blob/master/demo/chat/app.js

      io.use(function (socket, next) {
       var handshake = socket.handshake;
        if (handshake.headers.cookie) {
        cookieParser()(handshake, {}, function (err) {
        handshake.sessionID = connect.utils.parseSignedCookie(handshake.cookies[config.session.key], config.session.secret);
          handshake.sessionStore = config.session.store;
          handshake.sessionStore.get(handshake.sessionID, function (err, data) {
            if (err) return next(err);
            if (!data) return next(new Error('Invalid Session'));
            handshake.session = new session.Session(handshake, data);
            next();
          });
         });
       }
       else {
        next(new Error('Missing Cookies'));
       }
      });
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-02-28
        • 1970-01-01
        • 2014-10-21
        • 1970-01-01
        • 2012-02-10
        • 1970-01-01
        • 2015-04-30
        • 1970-01-01
        相关资源
        最近更新 更多