Come condividere sessioni con Socket.IO 1.xe Express 4.x?


88

Come posso condividere una sessione con Socket.io 1.0 ed Express 4.x? Uso un Redis Store, ma credo che non dovrebbe avere importanza. So di dover utilizzare un middleware per esaminare i cookie e recuperare la sessione, ma non so come. Ho cercato ma non sono riuscito a trovare alcun lavoro

    var RedisStore = connectRedis(expressSession);
    var session = expressSession({
        store: new RedisStore({
            client: redisClient
        }),
        secret: mysecret,
        saveUninitialized: true,
        resave: true
    });
    app.use(session);

    io.use(function(socket, next) {
        var handshake = socket.handshake;
        if (handshake.headers.cookie) {
            var str = handshake.headers.cookie;
            next();
        } else {
            next(new Error('Missing Cookies'));
        }
    });

Risposte:


213

La soluzione è sorprendentemente semplice. Semplicemente non è molto ben documentato. È possibile utilizzare il middleware della sessione express come middleware Socket.IO anche con un piccolo adattatore come questo:

sio.use(function(socket, next) {
    sessionMiddleware(socket.request, socket.request.res, next);
});

Ecco un esempio completo con express 4.x, Socket.IO 1.x e Redis:

var express = require("express");
var Server = require("http").Server;
var session = require("express-session");
var RedisStore = require("connect-redis")(session);

var app = express();
var server = Server(app);
var sio = require("socket.io")(server);

var sessionMiddleware = session({
    store: new RedisStore({}), // XXX redis server config
    secret: "keyboard cat",
});

sio.use(function(socket, next) {
    sessionMiddleware(socket.request, socket.request.res || {}, next);
});

app.use(sessionMiddleware);

app.get("/", function(req, res){
    req.session // Session object in a normal request
});

sio.sockets.on("connection", function(socket) {
  socket.request.session // Now it's available from Socket.IO sockets too! Win!
});


server.listen(8080);

16
Potresti aiutarmi con la tua soluzione? Ottengo solo questi dati {cookie: { path: '/', _expires: null, originalMaxAge: null, httpOnly: true, secure: true } }ma se stampo la sessione nei miei percorsi ottengo tutte le variabili di sessione che ho impostato (nome utente, id, ecc.)
Bobby Shark

7
Questo dovrebbe essere totalmente aggiunto ai loro documenti. La documentazione di autenticazione è super leggera come lo sono attualmente.
Bret

4
questo "funziona" per me, ma il mio ID di sessione Express non è lo stesso del mio ID di sessione socket.io ... forse non voglio che siano uguali comunque?
Alexander Mills

4
Questa soluzione ha funzionato alla grande! ... finché non ho avuto bisogno di salvare i dati nella sessione da un socket.on (), a quel punto ho scoperto che è solo un modo. C'è un modo per farlo funzionare in entrambi i modi?
iDVB

4
Ha funzionato benissimo con un paio di modifiche. Ma non sono riuscito a scrivere nella sessione da socket.io. Ho trovato un pacchetto NPM che ha soddisfatto tutte le mie esigenze e richiede circa lo stesso impegno di questa risposta per l'implementazione. npmjs.com/package/express-socket.io-session
Bacon Brad

6

Solo un mese e mezzo fa ho affrontato lo stesso problema e in seguito ho scritto un ampio post sul blog su questo argomento che va insieme a un'app demo completamente funzionante ospitata su GitHub. La soluzione si basa su moduli di nodo express-session , cookie-parser e connect-redis per collegare tutto. Ti consente di accedere e modificare le sessioni sia dal contesto REST che da Socket, il che è abbastanza utile.

Le due parti cruciali sono la configurazione del middleware:

app.use(cookieParser(config.sessionSecret));
app.use(session({
    store: redisStore,
    key: config.sessionCookieKey,
    secret: config.sessionSecret,
    resave: true,
    saveUninitialized: true
}));

... e configurazione del server SocketIO:

ioServer.use(function (socket, next) {
    var parseCookie = cookieParser(config.sessionSecret);
    var handshake = socket.request;

    parseCookie(handshake, null, function (err, data) {
        sessionService.get(handshake, function (err, session) {
            if (err)
                next(new Error(err.message));
            if (!session)
                next(new Error("Not authorized"));

            handshake.session = session;
            next();
        });
    });
});

Vanno insieme a un semplice modulo sessionService che ho creato che ti consente di eseguire alcune operazioni di base con le sessioni e quel codice ha questo aspetto:

var config = require('../config');

var redisClient = null;
var redisStore = null;

var self = module.exports = {
    initializeRedis: function (client, store) {
        redisClient = client;
        redisStore = store;
    },
    getSessionId: function (handshake) {
        return handshake.signedCookies[config.sessionCookieKey];
    },
    get: function (handshake, callback) {
        var sessionId = self.getSessionId(handshake);

        self.getSessionBySessionID(sessionId, function (err, session) {
            if (err) callback(err);
            if (callback != undefined)
                callback(null, session);
        });
    },
    getSessionBySessionID: function (sessionId, callback) {
        redisStore.load(sessionId, function (err, session) {
            if (err) callback(err);
            if (callback != undefined)
                callback(null, session);
        });
    },
    getUserName: function (handshake, callback) {
        self.get(handshake, function (err, session) {
            if (err) callback(err);
            if (session)
                callback(null, session.userName);
            else
                callback(null);
        });
    },
    updateSession: function (session, callback) {
        try {
            session.reload(function () {
                session.touch().save();
                callback(null, session);
            });
        }
        catch (err) {
            callback(err);
        }
    },
    setSessionProperty: function (session, propertyName, propertyValue, callback) {
        session[propertyName] = propertyValue;
        self.updateSession(session, callback);
    }
};

Poiché c'è più codice per l'intera cosa di questo (come inizializzare i moduli, lavorare con i socket e le chiamate REST sia sul lato client che sul lato server), non incollerò tutto il codice qui, puoi visualizzarlo su GitHub e puoi farci quello che vuoi.


4

express-socket.io-session

è una soluzione pronta per il tuo problema. Normalmente la sessione creata alla fine di socket.io ha un sid diverso da quelli creati in express.js

Prima di rendermene conto, mentre ci stavo lavorando per trovare la soluzione, ho trovato qualcosa di un po 'strano. Le sessioni create dall'istanza express.js erano accessibili all'estremità socket.io, ma lo stesso non era possibile per il contrario. E presto ho saputo che dovevo lavorare a modo mio attraverso la gestione di sid per risolvere il problema. Ma c'era già un pacchetto scritto per affrontare questo problema. È ben documentato e porta a termine il lavoro. Spero che sia d'aiuto


2

Usando la risposta di Bradley Lederholz, è così che l'ho fatto funzionare per me stesso. Fare riferimento alla risposta di Bradley Lederholz per ulteriori spiegazioni.

var app = express();
var server  = require('http').createServer(app);
var io = require('socket.io');
var cookieParse = require('cookie-parser')();
var passport = require('passport');
var passportInit = passport.initialize();
var passportSession = passport.session();
var session = require('express-session');
var mongoStore = require('connect-mongo')(session);
var mongoose = require('mongoose');
var sessionMiddleware = session({
  secret: 'some secret',
  key: 'express.sid',
  resave: true,
  httpOnly: true,
  secure: true,
  ephemeral: true,
  saveUninitialized: true,
  cookie: {},
  store:new mongoStore({
  mongooseConnection: mongoose.connection,
  db: 'mydb'
  });
});

app.use(sessionMiddleware);
io = io(server);
io.use(function(socket, next){
  socket.client.request.originalUrl = socket.client.request.url;
  cookieParse(socket.client.request, socket.client.request.res, next);
});

io.use(function(socket, next){
  socket.client.request.originalUrl = socket.client.request.url;
  sessionMiddleware(socket.client.request,   socket.client.request.res, next);
});

io.use(function(socket, next){
  passportInit(socket.client.request, socket.client.request.res, next);
});

io.use(function(socket, next){
  passportSession(socket.client.request, socket.client.request.res, next);
});

io.on('connection', function(socket){
  ...
});

... 
server.listen(8000);

Ha funzionato per me. Ho trovato il mio utente in socket.request.user
Milazi

0

L'ho risolto, ma non è perfetto. Non supporta i cookie firmati, ecc. Ho usato la funzione getcookie di express-session . La funzione modificata è la seguente:

    io.use(function(socket, next) {
        var cookie = require("cookie");
        var signature = require('cookie-signature');
        var debug = function() {};
        var deprecate = function() {};

        function getcookie(req, name, secret) {
            var header = req.headers.cookie;
            var raw;
            var val;

            // read from cookie header
            if (header) {
                var cookies = cookie.parse(header);

                raw = cookies[name];

                if (raw) {
                    if (raw.substr(0, 2) === 's:') {
                        val = signature.unsign(raw.slice(2), secret);

                        if (val === false) {
                            debug('cookie signature invalid');
                            val = undefined;
                        }
                    } else {
                        debug('cookie unsigned')
                    }
                }
            }

            // back-compat read from cookieParser() signedCookies data
            if (!val && req.signedCookies) {
                val = req.signedCookies[name];

                if (val) {
                    deprecate('cookie should be available in req.headers.cookie');
                }
            }

            // back-compat read from cookieParser() cookies data
            if (!val && req.cookies) {
                raw = req.cookies[name];

                if (raw) {
                    if (raw.substr(0, 2) === 's:') {
                        val = signature.unsign(raw.slice(2), secret);

                        if (val) {
                            deprecate('cookie should be available in req.headers.cookie');
                        }

                        if (val === false) {
                            debug('cookie signature invalid');
                            val = undefined;
                        }
                    } else {
                        debug('cookie unsigned')
                    }
                }
            }

            return val;
        }

        var handshake = socket.handshake;
        if (handshake.headers.cookie) {
            var req = {};
            req.headers = {};
            req.headers.cookie = handshake.headers.cookie;
            var sessionId = getcookie(req, "connect.sid", mysecret);
            console.log(sessionId);
            myStore.get(sessionId, function(err, sess) {
                console.log(err);
                console.log(sess);
                if (!sess) {
                    next(new Error("No session"));
                } else {
                    console.log(sess);
                    socket.session = sess;
                    next();
                }
            });
        } else {
            next(new Error("Not even a cookie found"));
        }
    });

    // Session backend config
    var RedisStore = connectRedis(expressSession);
    var myStore = new RedisStore({
        client: redisClient
    });
    var session = expressSession({
        store: myStore,
        secret: mysecret,
        saveUninitialized: true,
        resave: true
    });
    app.use(session);

0

Ora, la risposta originale accettata non funziona neanche per me. Come @ Rahil051, ho usato il modulo express-socket.io-session e funziona ancora. Questo modulo utilizza cookie-parser, per analizzare l'ID di sessione prima di entrare nel middleware della sessione express. Penso che sia sciocco per la risposta di @pootzko, @Mustafa e @ Kosar.

Sto usando questi moduli:

"dependencies": 
{
  "debug": "^2.6.1",
  "express": "^4.14.1",
  "express-session": "^1.15.1",
  "express-socket.io-session": "^1.3.2
  "socket.io": "^1.7.3"
}

controlla i dati in socket.handshake:

const debug = require('debug')('ws');
const sharedsession = require('express-socket.io-session');

module.exports = (server, session) => {
    const io = require('socket.io').listen(server);
    let connections = [];

    io.use(sharedsession(session, {
        autoSave: true,
    }));

    io.use(function (socket, next) {
        debug('check handshake %s', JSON.stringify(socket.handshake, null, 2));
        debug('check headers %s', JSON.stringify(socket.request.headers));
        debug('check socket.id %s', JSON.stringify(socket.id));
        next();
    });

    io.sockets.on('connection', (socket) => {
        connections.push(socket);
    });
};
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.