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
);
);
为方便起见,这里有一个完整的示例,使用 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);
【讨论】:
聚会有点晚了,但您的代码无法编译。最后一行之前缺少)
@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 & 会话共享的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 expressJS 提供 ReactJS 静态文件?
节点 websocket-server -- ETIMEDOUT 错误
无法从android okhttp3连接到节点websocket