【问题标题】:ExpressJS & Websocket & session sharingExpressJS & Websocket & 会话共享
【发布时间】:2012-08-24 08:12:08
【问题描述】:

我正在尝试制作一个基于 Node.js 的聊天应用程序。我想强制 websocket 服务器(ws 库)使用 ExpressJS 会话系统。不幸的是,我被卡住了。用于获取会话数据的 MemoryStore 哈希与 cookie 中的会话 ID 不同。有人可以解释一下我做错了什么吗?

Websocket 服务器代码部分:

module.exports = function(server, clients, express, store) {
  server.on('connection', function(websocket) {
    var username;

    function broadcast(msg, from) {...}

    function handleMessage(msg) {...}

    express.cookieParser()(websocket.upgradeReq, null, function(err) {
        var sessionID = websocket.upgradeReq.cookies['sid'];

            //I see same value in Firebug
        console.log(sessionID);

            //Shows all hashes in store
            //They're shorter than sessionID! Why?
        for(var i in store.sessions)
            console.log(i);

        store.get(sessionID, function(err, session) {
                websocket.on('message', handleMessage);

                //other code - won't be executed until sessionID in store

                websocket.on('close', function() {...});
        });
    });
});
}

存储对象定义:

var store = new express.session.MemoryStore({
    reapInterval: 60000 * 10
});

应用配置:

app.configure(function() {
    app.use(express.static(app.get("staticPath")));
    app.use(express.bodyParser());
    app.use(express.cookieParser());

    app.use(express.session({
        store: store,
        secret: "dO_ob",
        key: "sid"
    }));
});

部分主要代码:

var app = express();
var httpServer = http.createServer(app);
var websocketServer = new websocket.Server({server: httpServer});
httpServer.listen(80);

示例调试输出:

- websocket.upgradeReq.headers.cookie "sid=s%3A64a%2F6DZ4Mab8H5Q9MTKujmcw.U8PJJIR%2BOgONY57mZ1KtSPx6XSfcn%2FQPZ%2FfkGwELkmM"
- websocket.upgradeReq.cookies["sid"] "s:64a/6DZ4Mab8H5Q9MTKujmcw.U8PJJIR+OgONY57mZ1KtSPx6XSfcn/QPZ/fkGwELkmM"
- i "64a/6DZ4Mab8H5Q9MTKujmcw"

【问题讨论】:

  • 简单、丑陋的解决方法有帮助: sessionID = sessionID.match(/:[a-zA-Z0-9/+]+\./)[0].slice(1, -1),但我想解决这个问题。
  • 感谢您的解决方法,帮助了我:) 您是否找到了合适的解决方案?

标签: node.js session websocket express


【解决方案1】:

我发现这对我有用。不确定这是不是最好的方法。首先,初始化您的 express 应用程序:

// whatever your express app is using here...
var session = require("express-session");
var sessionParser = session({
    store: session_store,
    cookie: {secure: true, maxAge: null, httpOnly: true}
});
app.use(sessionParser);

现在,从 WS 连接显式调用会话中间件。如果您使用express-session 模块,中间件将自行解析cookie。否则,您可能需要先通过 cookie 解析中间件发送它。

如果您使用的是websocket 模块:

ws.on("request", function(req){
    sessionParser(req.httpRequest, {}, function(){
        console.log(req.httpRequest.session);
        // do stuff with the session here
    });
});

如果您使用的是ws 模块:

ws.on("connection", function(req){
    sessionParser(req.upgradeReq, {}, function(){
        console.log(req.upgradeReq.session);
        // do stuff with the session here
    });
});

为方便起见,这里有一个完整的示例,使用 expressexpress-sessionws

var app = require('express')();
var server = require("http").createServer(app);
var sessionParser = require('express-session')({
    secret:"secret",
    resave: true,
    saveUninitialized: true
});
app.use(sessionParser);

app.get("*", function(req, res, next) {
    req.session.working = "yes!";
    res.send("<script>var ws = new WebSocket('ws://localhost:3000');</script>");
});

var ws = new require("ws").Server({server: server});
ws.on("connection", function connection(req) {
    sessionParser(req.upgradeReq, {}, function(){
        console.log("New websocket connection:");
        var sess = req.upgradeReq.session;
        console.log("working = " + sess.working);
    });
});

server.listen(3000);

【讨论】:

  • 聚会有点晚了,但您的代码无法编译。最后一行之前缺少)
  • @matejkramny 谢谢,现在修复
  • 什么是对象ws
  • @AikonMogwai 这是你的 websocket 服务器对象。例如var WSServer = require("websocket").server; var ws = new WSServer(options);
  • sgeproject.narod.ru/node/app.js 是我的代码示例(express + ws),但on request 永远不会被提升。
【解决方案2】:

我能够得到这个工作。我认为您需要在 cookieParser 而不是会话存储上指定秘密。

我的应用示例:

var app = express();
var RedisStore = require('connect-redis')(express);
var sessionStore = new RedisStore();
var cookieParser = express.cookieParser('some secret');

app.use(cookieParser);
app.use(express.session({store: sessionStore}));


wss.on('connection', function(rawSocket) {

  cookieParser(rawSocket.upgradeReq, null, function(err) {
    var sessionID = rawSocket.upgradeReq.signedCookies['connect.sid'];
    sessionStore.get(sessionID, function(err, sess) {
      console.log(sess);
    });
  });

});

【讨论】:

    【解决方案3】:

    2022 年 2 月更新:

    verifyClientnow discouragedan issue comment 中描述了执行此操作的新方法。

    请参阅example code for session parsing and verification 以获取完整的使用示例。验证函数示例:

    server.on('upgrade', function (request, socket, head) {
      console.log('Parsing session from request...');
    
      sessionParser(request, {}, () => {
        if (!request.session.userId) {
          socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
          socket.destroy();
          return;
        }
    
        console.log('Session is parsed!');
    
        wss.handleUpgrade(request, socket, head, function (ws) {
          wss.emit('connection', ws, request);
        });
      });
    });
    

    原答案:

    ws 的 3.2.0 版中,您必须做一些不同的事情。

    ws repo 中有一个full working example 的 express session 解析,专门使用了一个新功能verifyClient

    非常简短的使用总结:

    const sessionParser = session({
      saveUninitialized: false,
      secret: '$eCuRiTy',
      resave: false
    })
    
    const server = http.createServer(app)
    const wss = new WebSocket.Server({
      verifyClient: (info, done) => {
        console.log('Parsing session from request...')
        sessionParser(info.req, {}, () => {
          console.log('Session is parsed!')
          done(info.req.session.userId)
        })
      },
      server
    })
    
    wss.on('connection', (ws, req) => {
      ws.on('message', (message) => {
        console.log(`WS message ${message} from user ${req.session.userId}`)
      })
    })
    

    【讨论】:

    • 我在上面链接的示例中的任何地方都没有看到 verifyClient
    • @PirateApp 它在第 9 行定义。
    • @PirateApp 自编写此答案以来似乎发生了很多事情,但 verifyClient 函数仍然有效。但是,现在似乎有新的方法来处理这个问题。我会更新这篇文章,澄清一下。
    【解决方案4】:

    WS v3.0.0 及更高版本已更改行为,因此这些版本的给定答案不会开箱即用。对于当前版本,连接方法的签名是 [function(socket, request)] 并且套接字不再包含对请求的引用。

    ws.on(
        'connection',
        function (socket, req)
        {
            sessionParser(
                req,
                {},
                function()
                {
                    console.log(req.session);
                }
            );
        }
    );
    

    【讨论】:

      【解决方案5】:

      目前,以下是我的工作正常的解决方法。我只是不知道它的缺点和安全性。如果它没有会话,我只是阻止服务器监听。 (Share session from express-session to ws)

      不过我还没有完全测试过。

      var http = require('http');
      var express = require('express');
      var expressSession = require('express-session');
      var router = express.Router();
      var app = express();
      
      const server = http.createServer(app);
      
      router.get('/', function(req, res, next) {
          if(req.session.user_id) {
              // Socket authenticated
              server.listen(8080, function listening(){});
          }
      });
      

      【讨论】:

        猜你喜欢
        • 2012-05-15
        • 1970-01-01
        • 2017-04-06
        • 1970-01-01
        • 2011-09-12
        • 2016-06-05
        • 2015-08-28
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多