ExpressJS & Websocket & 会话共享

Posted

技术标签:

【中文标题】ExpressJS & Websocket & 会话共享【英文标题】:ExpressJS & Websocket & session sharing 【发布时间】: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),但我想解决这个问题。 感谢您的解决方法,帮助了我:) 您是否找到了合适的解决方案? 【参考方案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 月更新:

verifyClient 是 now discouraged。 an 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 行定义。 这里没有 verifyClient github.com/websockets/ws/blob/master/examples/… @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());
    
);

【讨论】:

以上是关于ExpressJS & Websocket & 会话共享的主要内容,如果未能解决你的问题,请参考以下文章

为 Express 定义 Websocket 路由

如何使用 expressJS 提供 ReactJS 静态文件?

节点 websocket-server -- ETIMEDOUT 错误

无法从android okhttp3连接到节点websocket

如何在 Chrome 中加载下一页之前断开 websocket

ExpressJS:承诺和错误处理中间件