Wie teile ich Sitzungen mit Socket.IO 1.x und Express 4.x?

88

Wie kann ich eine Sitzung mit Socket.io 1.0 und Express 4.x teilen? Ich benutze einen Redis Store, aber ich glaube, das sollte keine Rolle spielen. Ich weiß, dass ich eine Middleware verwenden muss, um Cookies anzusehen und Sitzungen abzurufen, aber ich weiß nicht wie. Ich suchte, konnte aber keine Arbeit finden

    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'));
        }
    });
Mustafa
quelle

Antworten:

212

Die Lösung ist überraschend einfach. Es ist einfach nicht sehr gut dokumentiert. Mit einem kleinen Adapter wie dem folgenden kann die Express-Session-Middleware auch als Socket.IO-Middleware verwendet werden:

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

Hier ist ein vollständiges Beispiel mit Express 4.x, Socket.IO 1.x und 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);
Epeli
quelle
16
Könnten Sie mir bei Ihrer Lösung helfen? Ich erhalte nur diese Daten {Cookie: { path: '/', _expires: null, originalMaxAge: null, httpOnly: true, secure: true } }Aber wenn ich die Sitzung in meinen Routen
Bobby Shark
7
Dies sollte vollständig zu ihren Dokumenten hinzugefügt werden. Die Authentifizierungsdokumentation ist wie bisher sehr leicht.
Bret
4
Dies "funktioniert" für mich, aber meine Express-Sitzungs-ID stimmt nicht mit meiner socket.io-Sitzungs-ID überein ... Vielleicht möchte ich nicht, dass sie trotzdem identisch sind?
Alexander Mills
4
Diese Lösung hat super funktioniert! ... bis ich Daten aus einem Socket.on () in der Sitzung speichern musste, stelle ich fest, dass dies nur eine Möglichkeit ist. Gibt es eine Möglichkeit, es in beide Richtungen zum Laufen zu bringen?
IDVB
4
Dies funktionierte hervorragend mit ein paar Modifikationen. Aber ich konnte nicht von socket.io in die Sitzung schreiben. Ich habe ein NPM-Paket gefunden, das alle meine Anforderungen erfüllt und ungefähr den gleichen Aufwand erfordert wie die Implementierung dieser Antwort. npmjs.com/package/express-socket.io-session
Bacon Brad
6

Vor anderthalb Monaten habe ich mich mit dem gleichen Problem befasst und danach ein umfangreiches geschrieben Blog-Beitrag zu diesem Thema geschrieben, der mit einer voll funktionsfähigen Demo-App zusammenarbeitet, die auf GitHub gehostet wird. Die Lösung basiert auf Express-Session- , Cookie-Parser- und Connect-Redis- Knotenmodulen, um alles miteinander zu verbinden. Sie können auf Sitzungen sowohl im REST- als auch im Sockets-Kontext zugreifen und diese ändern, was sehr nützlich ist.

Die beiden entscheidenden Teile sind das Middleware-Setup:

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

... und SocketIO Server Setup:

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

Sie gehören zu einem einfachen sessionService-Modul, das ich erstellt habe und mit dem Sie einige grundlegende Operationen mit Sitzungen ausführen können. Der Code sieht folgendermaßen aus:

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

Da das Ganze mehr Code enthält (wie das Initialisieren von Modulen, das Arbeiten mit Sockets und REST-Aufrufen sowohl auf der Client- als auch auf der Serverseite), werde ich hier nicht den gesamten Code einfügen, Sie können ihn auf dem GitHub anzeigen und du kannst damit machen was du willst.

pootzko
quelle
4

express-socket.io-session

ist eine fertige Lösung für Ihr Problem. Normalerweise hat die am Ende von socket.io erstellte Sitzung eine andere Seite als die in express.js erstellten

Bevor ich diese Tatsache erkannte, als ich sie durcharbeitete, um die Lösung zu finden, fand ich etwas Seltsames. Auf die aus der express.js-Instanz erstellten Sitzungen konnte am Ende von socket.io zugegriffen werden, für das Gegenteil war dies jedoch nicht möglich. Und bald wurde mir klar, dass ich mich durch die Verwaltung von Sid arbeiten muss, um dieses Problem zu lösen. Es wurde jedoch bereits ein Paket geschrieben, um dieses Problem anzugehen. Es ist gut dokumentiert und erledigt die Arbeit. Ich hoffe es hilft

Rahil051
quelle
2

Mit der Antwort von Bradley Lederholz habe ich es für mich selbst zum Laufen gebracht. Weitere Erläuterungen finden Sie in der Antwort von Bradley Lederholz.

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);
Ali
quelle
Hat für mich gearbeitet. Fand meinen Benutzer in socket.request.user
Milazi
0

Ich habe es irgendwie gelöst, aber es ist nicht perfekt. Unterstützt keine signierten Cookies usw. Ich habe die getcookie-Funktion von express-session verwendet. Die modifizierte Funktion lautet wie folgt:

    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);
Mustafa
quelle
0

Jetzt funktioniert die ursprünglich akzeptierte Antwort auch bei mir nicht. Wie bei @ Rahil051 habe ich express-socket.io-session verwendet Modul und es funktioniert immer noch. Dieses Modul verwendet den Cookie-Parser, um die Sitzungs-ID vor dem Aufrufen der Middleware für Express-Sitzungen zu analysieren. Ich denke, die Antwort von @pootzko, @Mustafa und @ Kosar ist ziemlich ähnlich.

Ich benutze diese Module:

"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"
}

Überprüfen Sie die Daten 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);
    });
};
Pentatonisch
quelle