关于node.js:ExpressJS&Websocket和会话共享

ExpressJS & Websocket & session sharing

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

Websocket服务器代码部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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() {...});
        });
    });
});
}

存储对象定义:

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

应用配置:

1
2
3
4
5
6
7
8
9
10
11
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"
    }));
});

主要代码的一部分:

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

示例调试输出:

1
2
3
- 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"


我发现这对我有用。虽然不确定这是最好的方法。首先,初始化您的快速申请:

1
2
3
4
5
6
7
// 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模块:

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

如果您使用的是ws模块:

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

为了方便起见,下面是使用expressexpress-sessionws的完整示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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("var ws = new WebSocket('ws://localhost:3000');");
});

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


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

我的应用程序中的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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);
    });
  });

});


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

ws存储库中有一个完整的快速会话解析示例,特别是使用新功能verifyClient

一个非常简短的用法摘要:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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}`)
  })
})

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ws.on(
    'connection',
    function (socket, req)
    {
        sessionParser(
            req,
            {},
            function()
            {
                console.log(req.session);
            }
        );
    }
);

目前,下面是我的解决方法,它工作正常。我只是不知道它的缺点和安全性。如果没有会话,我只是阻止服务器监听。 (共享会话从快速会话到ws)

我还没有完全测试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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(){});
    }
});