socket.io and express 4 sessions

2019-01-07 06:10发布

I would like to access the express 4 session in my socket.io app. I'm kind of new with Node and I have some troubles implementing this functionality.

I found a npm module that allows access to the express 4 session : https://www.npmjs.org/package/session.socket.io-express4 or https://github.com/eiriklv/session.socket.io

If you look at my app.js code below, I'm doing something wrong in the session, sessionStore or cookieParser setup because I just can't get this module working.

// 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;

I tried multiples possibilities like :

  • Disabling https server.
  • Setting up a cookieParser object with secret phrase (so it "actually" exports the secret phrase to io = new SessionSockets(serverIO, sessionStore, cookieParser);)
  • Using minimal cookie options.

Anyway I'm a bit lost with this, any suggestions/critics are welcome.


UPDATE

Ok so after numerous tries I think I could get it work!

The problem is with the cookieParser initialization which the correct way seems to be :

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());

Notice that if I use var io = new SessionSockets(serverIO, sessionStore, cookieParser); (instead of cookieParser()) then it ain't working. That seems to be the problem.

If I use :

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"));

then the module crashes with the following error message :

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

But if I use :

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());

Then everything looks fine.

Now in the cookie-parser doc (https://github.com/expressjs/cookie-parser) it's saying you can pass a secret key to get the cookies signed. Which is something I'd like to have.

Could someone explain me the relation with the cookie-parser secret phrase and the session secret phrase ? Do they have to be the same/different ?

7条回答
神经病院院长
2楼-- · 2019-01-07 06:28

it gave me a hard time to find the right solution. Here is what works for me :

/*

    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

查看更多
地球回转人心会变
3楼-- · 2019-01-07 06:32

This may work express 4 / socket.io 1.X I grabbed this code form 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'));
 }
});
查看更多
Evening l夕情丶
4楼-- · 2019-01-07 06:35

Here's my solution for the following environment:

  • express 4.2.0
  • socket.io 1.1.0
  • cookie-parser 1.0.1
  • cookie-session 1.0.2

Code:

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);
    });
});

Then you can access the session from the socket's handshake:

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

For people wondering how/why this works:

  • We send the handshake request through the cookie parser so that cookies are available
  • Then we send the handshake through session middleware as if its a normal request
  • The middleware attaches session to the request
  • We use handshake because for all intents and purposes, it is a normal request, and the parser and session middleware can deal with it properly. This is why you must access the session through the handshake
查看更多
beautiful°
5楼-- · 2019-01-07 06:37

express 4.13.4 / socket.io 1.4.5

I browse all solutions and modules, but them all not working in my app. Finaly -

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);
});

working like a charm.

查看更多
孤傲高冷的网名
6楼-- · 2019-01-07 06:45

With the new express-session middleware all you have to do is to add the IO middleware:

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

A complete example would look like this:

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);
});

I created a super mini npm package socket.io-express-session which works as I explained above.

查看更多
Explosion°爆炸
7楼-- · 2019-01-07 06:46

express-socket.io-session is a ready-made solution for your problem. Normally the session created at socket.io end has different sid than the ones created in express.js

Before knowing that fact, when I was working through it to find the solution, I found something a bit weird. The sessions created from express.js instance were accessible at the socket.io end, but the same was not possible for the opposite. And soon I came to know that I have to work my way through managing sid to resolve that problem. But, there was already a package written to tackle such issue. It's well documented and gets the job done. Hope it helps

查看更多
登录 后发表回答