ExpressJS&Websocket&会话共享
我试图做一个基于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 });
应用configuration:
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);
示例debugging输出:
- 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"
我能够得到这个工作。 我认为你需要指定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); }); }); });
我发现这对我有用。 不知道这是做到这一点的最好方法。 首先,初始化您的快速申请:
// 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
模块,则中间件将自行parsingCookie。 否则,您可能需要先通过您的cookieparsing中间件发送它。
如果你使用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 }); });
为了您的方便,下面是一个完整的示例,使用express
, express-session
和ws
:
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);
目前,下面是我的解决方法,工作正常。 我只是不知道它的缺点和安全性。 如果它没有会话,我只是防止服务器听。 ( 从快速会议到ws分享会 )
我还没有完全testing这个。
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(){}); } });
WS v3.0.0及以上版本,已经改变了行为,所以给定的答案将不会在这些版本的框中工作。 对于当前版本,连接方法的签名是[function(socket,request)],并且套接字不再包含对请求的引用。
ws.on( 'connection', function (socket, req) { sessionParser( req, {}, function() { console.log(req.session); } ); } );
在ws
3.2.0版本中,你必须做一些改变。
在ws
repo中有一个完整的快速会话parsing示例 ,具体使用了一个新特性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}`) }) })