【问题标题】:How to share sessions with Socket.IO 1.x and Express 4.x?如何与 Socket.IO 1.x 和 Express 4.x 共享会话?
【发布时间】:2014-10-21 08:45:37
【问题描述】:

如何与 Socket.io 1.0 和 Express 4.x 共享会话?我使用 Redis Store,但我相信这不重要。我知道我必须使用中间件来查看 cookie 并获取会话,但不知道如何。我搜索但找不到任何工作

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

【问题讨论】:

    标签: node.js session express socket.io


    【解决方案1】:

    PostgreSQL 的工作示例和解决获取“具有空会话信息且只有 cookie 的对象”的问题:

    服务器端(Node.js + PostgreSQL):

    const express = require("express");
    const Server = require("http").Server;
    const session = require("express-session");
    const pg = require('pg');
    const expressSession = require('express-session');
    const pgSession = require('connect-pg-simple')(expressSession);
      
    const PORT = process.env.PORT || 5000;
    
    const pgPool = new pg.Pool({
        user : 'user',
        password : 'pass',
        database : 'DB',
        host : '127.0.0.1',
        connectionTimeoutMillis : 5000,
        idleTimeoutMillis : 30000
    });
    
    const app = express();
    
    var ioServer = require('http').createServer(app);
    var io = require('socket.io')(ioServer);
    
    var sessionMiddleware = session({
        store: new RedisStore({}), // XXX redis server config
        secret: "keyboard cat",
    });
    
    io.use(function(socket, next) {
       session(socket.request, {}, next);
    });
    
    app.use(session);
    
    io.on("connection", socket => {
      const ioSession = socket.request.session;
      socket.on('userJoined', (data) => {
        console.log('---ioSession---', ioSession)
      }
    }
    

    客户端(react-native 应用程序): 要解决获取“空会话对象”的问题,您需要添加 withCredentials: true

    this.socket = io(`http://${ip}:5000`, {
      withCredentials: true,
    });
    

    【讨论】:

      【解决方案2】:

      解决方案非常简单。它只是没有很好的记录。也可以将 express 会话中间件用作 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);
      

      【讨论】:

      • 你能帮我解决你的问题吗?我只得到这个数据 { cookie: { path: '/', _expires: null, originalMaxAge: null, httpOnly: true, secure: true } } 但是如果我在我的路由中打印会话,我会得到我设置的所有会话变量(用户名、ID 等)。
      • 这应该完全添加到他们的文档中。身份验证文档目前非常简单。
      • 这对我“有效”,但我的 Express 会话 ID 与我的 socket.io 会话 ID 不同...也许我真的不希望它们相同?跨度>
      • 这个解决方案效果很好! ...直到我需要从 socket.on() 中将数据保存到会话中,那时我发现它只有一种方式。有没有办法让它双向工作?
      • 经过一些修改后效果很好。但我无法从 socket.io 写入会话。我找到了一个 NPM 包,它可以满足我的所有需求,并且需要与这个答案相同的努力来实现。 npmjs.com/package/express-socket.io-session
      【解决方案3】:

      现在,最初接受的答案对我也不起作用。与@Rahil051 相同,我使用了express-socket.io-session 模块,它仍然有效。该模块使用 cookie-parser,在进入 express-session 中间件之前解析 session id。 我认为@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);
          });
      };
      

      【讨论】:

        【解决方案4】:

        使用 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 中找到我的用户
        【解决方案5】:

        express-socket.io-session

        是针对您的问题的现成解决方案。通常在 socket.io 端创建的会话与在 express.js 中创建的会话具有不同的 sid

        在知道这个事实之前,当我通过它寻找解决方案时,我发现了一些奇怪的东西。从 express.js 实例创建的会话可以在 socket.io 端访问,但相反的情况是不可能的。很快我就知道我必须通过管理 sid 来解决这个问题。但是,已经编写了一个包来解决这个问题。它有据可查,可以完成工作。希望对你有帮助

        【讨论】:

          【解决方案6】:

          就在一个半月前,我处理了同样的问题,然后就这个主题写了一个广泛的blog post,与托管在 GitHub 上的完整工作的demo app 一起。该解决方案依赖于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 上,你可以用它做任何你想做的事情。

          【讨论】:

            【解决方案7】:

            我已经解决了它,但它并不完美。不支持签名 cookie 等。我使用了 express-session 的 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);
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-03-26
              • 2014-06-23
              • 2012-05-15
              • 2018-05-30
              • 1970-01-01
              • 2018-08-17
              相关资源
              最近更新 更多