web-dev-qa-db-fra.com

Comment partager des sessions avec Socket.IO 1.x et Express 4.x?

Comment partager une session avec Socket.io 1.0 et Express 4.x? J'utilise un magasin Redis, mais je crois que cela ne devrait pas avoir d'importance. Je sais que je dois utiliser un middleware pour consulter les cookies et récupérer une session, mais je ne sais pas comment. J'ai cherché mais n'ai pas trouvé de travail 

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

La solution est étonnamment simple. Ce n'est tout simplement pas très bien documenté. Il est possible d'utiliser le middleware de session express comme middleware Socket.IO également avec un petit adaptateur comme celui-ci:

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

Voici un exemple complet avec express 4.x, Socket.IO 1.x et 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);
181
Epeli

Il y a à peine un mois et demi, je m'occupais du même problème et écrivais ensuite un vaste { article de blog } sur ce sujet, associé à une application de démonstration entièrement opérationnelle hébergée sur GitHub. La solution repose sur les modules de nœuds express-session , analyseur de cookie _ et connect-redis _ _. Il vous permet d'accéder aux sessions et de les modifier à partir du contexte REST et du contexte Sockets, ce qui est très utile.

Les deux parties cruciales sont la configuration du middleware: 

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

... et la configuration du serveur 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();
        });
    });
});

Ils vont avec un module sessionService simple que j'ai créé, qui vous permet de faire quelques opérations de base avec des sessions et que le code ressemble à ceci:

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

Comme il y a plus de code dans tout cela (comme initialiser des modules, travailler avec des sockets et des appels REST à la fois côté client et côté serveur), je ne vais pas coller tout le code ici, vous pouvez voir sur le GitHub et vous pouvez faire ce que vous voulez avec. 

4
pootzko

express-socket.io-session

est une solution toute faite pour votre problème. Normalement, la session créée dans socket.io end a un sid différent de ceux créés dans express.js

Avant de le savoir, en cherchant la solution, j'ai trouvé quelque chose d'un peu bizarre. Les sessions créées à partir de l'instance express.js étaient accessibles à la fin du fichier socket.io, mais la même chose n'était pas possible pour le contraire. Et bientôt, j'ai compris que je devais me débrouiller pour gérer ce système afin de résoudre ce problème. Mais, il y avait déjà un paquet écrit pour s'attaquer à ce problème. C'est bien documenté et fait le travail. J'espère que ça aide

3
Rahil051

En utilisant la réponse de Bradley Lederholz, voici comment je l’ai fait fonctionner pour moi-même. Veuillez vous reporter à la réponse de Bradley Lederholz pour plus d'explications.

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);
1
Ali

Je l'ai un peu résolu, mais ce n'est pas parfait. Ne prend pas en charge les cookies signés, etc. J'ai utilisé la fonction getcookie de express-session . La fonction modifiée est la suivante:

    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
Mustafa

Maintenant, la réponse acceptée à l'origine ne fonctionne pas pour moi non plus. Identique à @ Rahil051, j'ai utilisé express-socket.io-session module, et cela fonctionne toujours. Ce module utilise cookie-parser pour analyser l'identifiant de session avant d'entrer dans le middleware de session express . Je pense que c'est silmiar à la réponse de @pootzko, @Mustafa et @ Kosar.

J'utilise ces modules:

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

vérifier les données dans 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);
    });
};
0
Pentatonic