Bagaimana cara berbagi sesi dengan Socket.IO 1.x dan Express 4.x?


89

Bagaimana saya bisa berbagi sesi dengan Socket.io 1.0 dan Express 4.x? Saya menggunakan Toko Redis, tapi saya yakin itu tidak masalah. Saya tahu saya harus menggunakan middleware untuk melihat cookie dan mengambil sesi, tetapi tidak tahu caranya. Saya mencari tetapi tidak dapat menemukan yang berfungsi

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

Jawaban:


215

Solusinya sangat sederhana. Itu hanya tidak didokumentasikan dengan baik. Dimungkinkan juga untuk menggunakan middleware sesi ekspres sebagai middleware Socket.IO juga dengan adaptor kecil seperti ini:

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

Berikut adalah contoh lengkap dengan express 4.x, Socket.IO 1.x dan 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);

17
Bisakah Anda membantu saya dengan solusi Anda? Saya hanya mendapatkan data ini {cookie: { path: '/', _expires: null, originalMaxAge: null, httpOnly: true, secure: true } }Tetapi jika saya mencetak sesi di rute saya, saya mendapatkan semua variabel sesi yang telah saya siapkan (nama pengguna, id, dll ..)
Bobby Shark

7
Ini harus benar-benar ditambahkan ke dokumen mereka. Dokumentasi otentikasi sangat ringan seperti saat ini.
Bret

4
ini "berfungsi" untuk saya, tetapi ID sesi Ekspres saya tidak sama dengan ID sesi socket.io saya ... mungkin saya tidak benar-benar ingin keduanya sama?
Alexander Mills

4
Solusi ini bekerja BESAR! ... sampai saya perlu menyimpan data ke sesi dari dalam socket.on () pada saat itu saya menemukan bahwa itu hanya satu cara. Apakah ada cara untuk membuatnya bekerja dua arah?
iDVB

4
Ini bekerja dengan baik dengan beberapa modifikasi. Tetapi saya tidak bisa menulis ke sesi dari socket.io. Saya menemukan paket NPM yang memenuhi semua kebutuhan saya dan membutuhkan upaya yang sama untuk menerapkan jawaban ini. npmjs.com/package/express-socket.io-session
Bacon Brad

6

Hanya satu setengah bulan yang lalu saya menangani masalah yang sama dan setelah itu menulis posting blog ekstensif tentang topik ini yang sejalan dengan aplikasi demo yang berfungsi penuh yang dihosting di GitHub. Solusinya bergantung pada modul node sesi-ekspres , cookie-parser, dan connect-redis untuk mengikat semuanya. Ini memungkinkan Anda untuk mengakses dan memodifikasi sesi dari konteks REST dan Sockets yang cukup berguna.

Dua bagian penting adalah pengaturan middleware:

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

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

Mereka pergi bersama dengan modul sessionService sederhana yang saya buat yang memungkinkan Anda melakukan beberapa operasi dasar dengan sesi dan kode itu terlihat seperti ini:

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

Karena ada lebih banyak kode untuk semuanya daripada ini (seperti menginisialisasi modul, bekerja dengan soket dan panggilan REST pada klien dan sisi server), saya tidak akan menempelkan semua kode di sini, Anda dapat melihatnya di GitHub dan Anda dapat melakukan apa pun yang Anda inginkan dengannya.


4

express-socket.io-session

adalah solusi siap pakai untuk masalah Anda. Biasanya sesi yang dibuat di socket.io end memiliki sid yang berbeda dari yang dibuat di express.js

Sebelum mengetahui fakta itu, ketika saya sedang mengerjakannya untuk menemukan solusinya, saya menemukan sesuatu yang agak aneh. Sesi yang dibuat dari instance express.js dapat diakses di ujung socket.io, tetapi hal yang sama tidak mungkin terjadi sebaliknya. Dan segera saya mengetahui bahwa saya harus bekerja dengan cara saya mengelola sid untuk menyelesaikan masalah itu. Tapi, sudah ada paket yang dibuat untuk mengatasi masalah tersebut. Itu didokumentasikan dengan baik dan menyelesaikan pekerjaan. Semoga membantu


2

Menggunakan jawaban Bradley Lederholz, inilah cara saya membuatnya berhasil untuk diri saya sendiri. Silakan merujuk pada jawaban Bradley Lederholz, untuk penjelasan lebih lanjut.

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

Bekerja untuk saya. Ditemukan pengguna saya di socket.request.user
Milazi

0

Saya sudah menyelesaikannya, tapi tidak sempurna. Tidak mendukung cookie yang ditandatangani, dll. Saya menggunakan fungsi getcookie sesi ekspres . Fungsi yang dimodifikasi adalah sebagai berikut:

    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

Sekarang, jawaban asli yang diterima juga tidak berhasil untuk saya. Sama seperti @ Rahil051, saya menggunakan modul express-socket.io-session , dan masih berfungsi. Modul ini menggunakan cookie-parser, untuk mengurai id sesi sebelum memasuki middleware sesi ekspres. Saya pikir itu konyol untuk jawaban @pootzko, @Mustafa dan @ Kosar.

Saya menggunakan modul ini:

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

lihat data di 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);
    });
};
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.