Làm thế nào để chia sẻ phiên với Socket.IO 1.x và Express 4.x?


88

Làm cách nào để chia sẻ một phiên với Socket.io 1.0 và Express 4.x? Tôi sử dụng Redis Store, nhưng tôi tin rằng nó không thành vấn đề. Tôi biết mình phải sử dụng phần mềm trung gian để xem cookie và tìm nạp phiên, nhưng không biết làm thế nào. Tôi đã tìm kiếm nhưng không thể tìm thấy bất kỳ hoạt động nào

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

Câu trả lời:


213

Giải pháp đơn giản một cách đáng ngạc nhiên. Nó chỉ không được ghi chép đầy đủ. Cũng có thể sử dụng phần mềm trung gian phiên express làm phần mềm trung gian Socket.IO với một bộ điều hợp nhỏ như sau:

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

Đây là một ví dụ đầy đủ với express 4.x, Socket.IO 1.x và 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
Bạn có thể giúp tôi với giải pháp của bạn? Tôi chỉ nhận được dữ liệu này {cookie: { path: '/', _expires: null, originalMaxAge: null, httpOnly: true, secure: true } }Nhưng nếu tôi in phiên trong các tuyến đường của mình, tôi sẽ nhận được tất cả các biến phiên mà tôi đã thiết lập (tên người dùng, id, v.v.)
Bobby Shark

7
Điều này hoàn toàn sẽ được thêm vào tài liệu của họ. Tài liệu xác thực siêu nhẹ như hiện tại.
Bret

4
điều này "hoạt động" đối với tôi, nhưng ID phiên Express của tôi không giống với ID phiên socket.io của tôi ... có lẽ tôi không thực sự muốn chúng giống nhau?
Alexander Mills

4
Giải pháp này hoạt động TUYỆT VỜI! ... cho đến khi tôi cần lưu dữ liệu vào phiên từ bên trong socket.on () tại thời điểm đó tôi thấy rằng đó là một cách duy nhất. Có cách nào để làm cho nó hoạt động theo cả hai cách?
iDVB

4
Điều này hoạt động tốt với một vài sửa đổi. Nhưng tôi không thể ghi vào phiên từ socket.io. Tôi đã tìm thấy một gói NPM đáp ứng mọi nhu cầu của tôi và cần nỗ lực tương tự như câu trả lời này để triển khai. npmjs.com/package/express-socket.io-session
Bacon Brad.

6

Chỉ một tháng rưỡi trước, tôi đã giải quyết vấn đề tương tự và sau đó đã viết một bài đăng blog mở rộng về chủ đề này cùng với một ứng dụng demo hoạt động hoàn toàn được lưu trữ trên GitHub. Giải pháp dựa trên các mô-đun nút express-session , cookie-parserconnect-redis để kết nối mọi thứ. Nó cho phép bạn truy cập và sửa đổi các phiên từ cả ngữ cảnh REST và Sockets, khá hữu ích.

Hai phần quan trọng là thiết lập phần mềm trung gian:

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

... và thiết lập máy chủ 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();
        });
    });
});

Chúng đi cùng nhau với một mô-đun sessionService đơn giản mà tôi đã tạo cho phép bạn thực hiện một số thao tác cơ bản với các phiên và mã đó trông giống như sau:

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

Vì có nhiều mã hơn cho toàn bộ điều này (như khởi tạo mô-đun, làm việc với các ổ cắm và lệnh gọi REST trên cả phía máy khách và phía máy chủ), tôi sẽ không dán tất cả mã ở đây, bạn có thể xem nó trên GitHub và bạn có thể làm bất cứ điều gì bạn muốn với nó.


4

express-socket.io-session

là một giải pháp sẵn sàng cho vấn đề của bạn. Thông thường, phiên được tạo tại socket.io end có sid khác với phiên được tạo trong express.js

Trước khi biết sự thật đó, khi tôi đang làm việc để tìm ra giải pháp, tôi thấy có điều gì đó hơi kỳ lạ. Các phiên được tạo từ cá thể express.js có thể truy cập được ở cuối socket.io, nhưng ngược lại thì không thể truy cập được. Và tôi sớm biết rằng tôi phải làm việc theo cách của mình thông qua quản lý sid để giải quyết vấn đề đó. Nhưng, đã có một gói được viết để giải quyết vấn đề như vậy. Nó được ghi chép đầy đủ và hoàn thành công việc. Hy vọng nó giúp


2

Sử dụng câu trả lời của Bradley Lederholz, đây là cách tôi làm cho nó hoạt động cho chính mình. Hãy tham khảo câu trả lời của Bradley Lederholz, để được giải thích rõ hơn.

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

Đã làm cho tôi. Tìm thấy người dùng của tôi trong socket.request.user
Milazi

0

Tôi đã giải quyết được nó, nhưng nó không hoàn hảo. Không hỗ trợ cookie đã ký, v.v. Tôi đã sử dụng chức năng getcookie của express-session . Chức năng được sửa đổi như sau:

    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

Bây giờ, câu trả lời được chấp nhận ban đầu cũng không phù hợp với tôi. Tương tự như @ Rahil051, tôi đã sử dụng mô-đun express-socket.io-session và nó vẫn hoạt động. Mô-đun này sử dụng trình phân tích cú pháp cookie, để phân tích cú pháp id phiên trước khi nhập phần mềm trung gian phiên nhanh. Tôi nghĩ đó là câu trả lời của @pootzko, @Mustafa và @ Kosar.

Tôi đang sử dụng các mô-đun này:

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

kiểm tra dữ liệu trong 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);
    });
};
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.