จะแชร์เซสชันกับ Socket.IO 1.x และ Express 4.x ได้อย่างไร?


91

ฉันจะแชร์เซสชันกับ Socket.io 1.0 และ Express 4.x ได้อย่างไร ฉันใช้ Redis Store แต่ฉันเชื่อว่ามันไม่สำคัญ ฉันรู้ว่าฉันต้องใช้มิดเดิลแวร์เพื่อดูคุกกี้และเรียกเซสชัน แต่ไม่รู้ว่าจะทำอย่างไร ฉันค้นหา แต่ไม่พบการทำงานใด ๆ

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

คำตอบ:


219

วิธีแก้ปัญหานั้นง่ายอย่างน่าประหลาดใจ มันเป็นเพียงเอกสารที่ไม่ดีมาก เป็นไปได้ที่จะใช้มิดเดิลแวร์ของเซสชันด่วนเป็นมิดเดิลแวร์ Socket.IO ด้วยอะแดปเตอร์ขนาดเล็กเช่นนี้:

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

นี่คือตัวอย่างแบบเต็มของ express 4.x, Socket.IO 1.x และ 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);

18
คุณช่วยฉันแก้ปัญหาได้ไหม ฉันได้รับข้อมูลนี้เท่านั้น {คุกกี้: { path: '/', _expires: null, originalMaxAge: null, httpOnly: true, secure: true } }แต่ถ้าฉันพิมพ์เซสชันในเส้นทางของฉันฉันจะได้รับตัวแปรเซสชันทั้งหมดที่ฉันตั้งค่าไว้ (ชื่อผู้ใช้ id ฯลฯ .. )
Bobby Shark

7
สิ่งนี้ควรถูกเพิ่มลงในเอกสารของพวกเขาทั้งหมด เอกสารรับรองความถูกต้องมีน้ำหนักเบามากอย่างที่เป็นอยู่ในปัจจุบัน
Bret

4
สิ่งนี้ "ใช้ได้" สำหรับฉัน แต่ ID เซสชัน Express ของฉันไม่เหมือนกับรหัสเซสชัน socket.io ของฉัน ... บางทีฉันอาจจะไม่ต้องการให้มันเหมือนเดิม?
Alexander Mills

4
วิธีนี้ใช้ได้ผลดีมาก! ... จนกระทั่งฉันจำเป็นต้องบันทึกข้อมูลลงในเซสชันจากภายในซ็อกเก็ตบน () ณ จุดนั้นฉันพบว่ามันมีทางเดียวเท่านั้น มีวิธีทำให้ใช้งานได้ทั้งสองวิธีหรือไม่?
iDVB

4
สิ่งนี้ใช้ได้ดีกับการปรับเปลี่ยนสองสามอย่าง แต่ฉันไม่สามารถเขียนไปยังเซสชันจาก socket.io ฉันพบแพ็คเกจ NPM ที่ตรงกับความต้องการทั้งหมดของฉันและใช้ความพยายามเช่นเดียวกับคำตอบนี้ในการนำไปใช้ npmjs.com/package/express-socket.io-session
Bacon Brad

6

เพียงหนึ่งเดือนครึ่งที่ผ่านมาฉันได้จัดการกับปัญหาเดียวกันและหลังจากนั้นก็เขียนบล็อกโพสต์มากมายในหัวข้อนี้ซึ่งใช้ร่วมกับแอปสาธิตที่ใช้งานได้อย่างสมบูรณ์ซึ่งโฮสต์บน GitHub โซลูชันนี้อาศัยโมดูลโหนดExpress-session , cookie-parserและconnect-redisเพื่อรวมทุกอย่างเข้าด้วยกัน ช่วยให้คุณสามารถเข้าถึงและแก้ไขเซสชันจากทั้งบริบท REST และ Sockets ซึ่งมีประโยชน์มาก

สองส่วนที่สำคัญคือการตั้งค่ามิดเดิลแวร์:

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

... และการตั้งค่าเซิร์ฟเวอร์ 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();
        });
    });
});

พวกเขาทำงานร่วมกับโมดูล sessionService แบบง่ายที่ฉันสร้างขึ้นซึ่งช่วยให้คุณสามารถดำเนินการขั้นพื้นฐานกับเซสชันได้และรหัสนั้นมีลักษณะดังนี้:

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

เนื่องจากมีโค้ดมากกว่านี้ทั้งหมด (เช่นการเริ่มต้นโมดูลการทำงานกับซ็อกเก็ตและการเรียก REST ทั้งฝั่งไคลเอนต์และฝั่งเซิร์ฟเวอร์) ฉันจะไม่วางโค้ดทั้งหมดที่นี่คุณสามารถดูได้ใน GitHub และคุณสามารถทำอะไรก็ได้ที่คุณต้องการ


4

express-socket.io-session

เป็นโซลูชันสำเร็จรูปสำหรับปัญหาของคุณ โดยปกติเซสชันที่สร้างที่ปลาย socket.io จะมี sid ต่างจากที่สร้างใน express.js

ก่อนที่จะทราบความจริงนั้นเมื่อฉันพยายามหาทางแก้ปัญหานั้นฉันพบว่ามีอะไรแปลก ๆ เล็กน้อย เซสชันที่สร้างขึ้นจากอินสแตนซ์ express.js สามารถเข้าถึงได้ที่ส่วนท้าย socket.io แต่ในทางตรงกันข้ามไม่สามารถทำได้ และในไม่ช้าฉันก็รู้ว่าฉันต้องพยายามจัดการกับ sid เพื่อแก้ไขปัญหานั้น แต่มีแพ็คเกจที่เขียนขึ้นเพื่อแก้ไขปัญหาดังกล่าวแล้ว มีการจัดทำเอกสารอย่างดีและทำงานให้ลุล่วง หวังว่าจะช่วยได้


2

จากคำตอบของ Bradley Lederholz นี่คือวิธีที่ฉันทำให้มันได้ผลสำหรับตัวเอง โปรดดูคำตอบของ 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);

ทำงานให้ฉัน พบผู้ใช้ของฉันใน socket.request.user
Milazi

0

ฉันได้แก้ไขแล้ว แต่มันไม่สมบูรณ์แบบ ไม่รองรับคุกกี้ที่ลงนามเป็นต้นฉันใช้ฟังก์ชัน getcookie ของเซสชันด่วน ฟังก์ชันที่แก้ไขมีดังนี้:

    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

ตอนนี้คำตอบเดิมที่ยอมรับก็ใช้ไม่ได้กับฉันเช่นกัน เหมือนกับ @ Rahil051 ฉันใช้โมดูลexpress-socket.io-sessionและยังใช้งานได้ โมดูลนี้ใช้ตัวแยกวิเคราะห์คุกกี้เพื่อแยกวิเคราะห์รหัสเซสชันก่อนที่จะเข้าสู่มิดเดิลแวร์ของเซสชันด่วน ฉันคิดว่ามันเป็นเรื่องน่ายินดีสำหรับคำตอบของ @pootzko, @Mustafa และ @ Kosar

ฉันใช้โมดูลเหล่านี้:

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

ตรวจสอบข้อมูลใน 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);
    });
};
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.