socket.io 和 express 4 个会话

Posted

技术标签:

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

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

我找到了一个允许访问 express 4 会话的 npm 模块:https://www.npmjs.org/package/session.socket.io-express4 或 https://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 密码短语和会话密码短语的关系吗?它们必须相同/不同吗?

【问题讨论】:

另请参阅此相关答案:***.com/a/24859515/1407170 【参考方案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,它的工作原理与我上面解释的一样。

【讨论】:

这个解决方案效果好一点:***.com/a/53641007/630169【参考方案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'));
 
);

【讨论】:

以上是关于socket.io 和 express 4 个会话的主要内容,如果未能解决你的问题,请参考以下文章

与 socket.io 一起使用时 express-session 未设置会话 cookie

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

无法从带有 Socket.IO 的 cookie 中获取 Express 会话 ID

如何处理 Socket.io 和 Express 的会话?

如何访问socket.io连接中的会话对象[重复]

Socket.io Express 3 次会议