Node.js、ws和express-session:如何从ws.upgradeReq获取会话对象

9

我有一个基于express、express-session和ws构建的应用程序(用于支持websocket)。

我使用了以下代码来启用session中间件:

app.use(session({secret: 'mysecret',resave: true,saveUninitialized: true,cookie: {}}));

我可以通过以下方式在get处理程序中读取和保存会话变量:

app.get('/', function (req, res) {
  res.send('Hello World!');
  console.log(req.session.test);
  req.session.test="OK";
  req.session.save();
});

第一次打印“undefined”,第二次按预期打印“OK”。

现在我有一个由express处理的websocket服务器,它在同一个应用程序中处理,我想在ws.on("connection",...)ws.on("message",...)中获取req.session对象。

问题在于没有对原始req对象的引用,只有对websocket连接处理程序的包装器upgradeReq的引用。 检查这个upgradeReq,我可以在ws.upgradeReq.headers.cookie中找到connect.sid,其中包含会话cookie值。

wss.on('connection', function connection(ws) {
  var location = url.parse(ws.upgradeReq.url, true);
  console.log(ws.upgradeReq.headers.cookie);
  //....
});

我认为有可能以某种方式获取原始的req.session

我该如何实现这一点?

2个回答

10

好的,我已经发现了一种未记录的方法,通过研究express-session的源代码。

首先,您需要将cookiecookie-parser作为依赖项。

所以,导入这些模块。

var cookie = require('cookie');
var cookieParser = require('cookie-parser')

然后,诀窍在于在ws“连接”事件处理程序中解码已签名的cookie,即websocket握手时。 首先,您需要从cookie中解码connect.sid,这是会话ID(仅在使用express-session进行会话管理时)。

wss.on('connection', function connection(ws) {
    var cookies=cookie.parse(ws.upgradeReq.headers.cookie);
    var sid=cookieParser.signedCookie(cookies["connect.sid"],secret);
    ...
请确保密钥与您在express-session中为签名设置的密钥相同(即app.use(session({secret: secret, resave: true, saveUninitialized: true, cookie: {},store: store}));)。
此时,您已经通过sid获得了Session ID,接下来需要从express-session中异步传递存储对象以获取Session对象(有很多种类型,请参阅文档。目前我只使用了MemoryStore of express-session进行测试)。
为了获取会话,请使用上述代码,在上述行之后。
...
store.get(sid,function(err, ss){
        store.createSession(ws.upgradeReq,ss)
    });

回调函数直接从存储中返回会话对象(ss),但它是单独的且没有方法(只是一个反序列化的JSON)。因此出于这个原因,我们再次使用store.createSession,向其传递upgradeReq和ss对象,这样我就拥有了一个具有所有已记录方法的会话对象(所以我也可以在ws回调中修改会话)。

新的会话对象可在以下位置获取:

ws.upgradeReq.session

注意: 直到存储异步返回ss对象之前,会话对象不可用,因此最好在传递给store.get的回调函数中完成websocket事件绑定(如on('message',...))。

以下是完整代码:

wss.on('connection', function connection(ws) {
    var location = url.parse(ws.upgradeReq.url, true);
    //get sessionID
    var cookies = cookie.parse(ws.upgradeReq.headers.cookie);
    var sid = cookieParser.signedCookie(cookies["connect.sid"], secret);
    //get the session object
    store.get(sid, function (err, ss) {
        //create the session object and append on upgradeReq
        store.createSession(ws.upgradeReq, ss)
        //setup websocket bindings
        ws.on('message', function incoming(message) {
            console.log('received: %s', message);
            //..........
        });

    });
});

现在,您可以使用ws和express-session一起管理会话数据。

我希望这能够有所帮助!


3
谢谢!这个方法对我也有用,特别是与护照一起使用时。在这种情况下,我最终可以在ws.upgradeReq.session.passport.user中找到用户。谢谢! - logidelic
1
你好, 我正在尝试做同样的事情,但是我不明白为什么ws.upgradeReq是未定义的。有人可以帮助我吗? - dritan
2
@dritan 最近在ws 3.0中出现了一个重大的变化。ws.upgradeReq已被删除。搜索“ws breaking change upgradeReq”将会得到更多信息。 - user1562766

8
我刚刚发现WebSocket的版本3.0.0移除了upgradeReq。对于alexroat的现有代码,可以采用以下解决办法:
wss.on('connection', function connection(ws, req) {
    ws.upgradeReq = req;
    var location = url.parse(ws.upgradeReq.url, true);
    //get sessionID
    var cookies = cookie.parse(ws.upgradeReq.headers.cookie);
    var sid = cookieParser.signedCookie(cookies["connect.sid"], secret);
    //get the session object
    store.get(sid, function (err, ss) {
        //create the session object and append on upgradeReq
        store.createSession(ws.upgradeReq, ss)
        //setup websocket bindings
        ws.on('message', function incoming(message) {
            console.log('received: %s', message);
            //..........
        });
    });
});

哇!你刚刚救了一个人的命。 - Milind Chaudhary

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接