socket.io 和 express 4 个会话
Posted
技术标签:
【中文标题】socket.io 和 express 4 个会话【英文标题】:socket.io and express 4 sessions 【发布时间】:2014-06-23 00:38:09 【问题描述】:我想在我的 socket.io 应用程序中访问 express 4 会话。 我是 Node 的新手,在实现这个功能时遇到了一些麻烦。
我找到了一个允许访问 express 4 会话的 npm 模块:https://www.npmjs.org/package/session.socket.io-express4 或 https://github.com/eiriklv/session.socket.io
如果您查看下面我的 app.js 代码,我在 session
、sessionStore
或 cookieParser
设置中做错了,因为我无法让这个模块工作。
// init modules
var express = require('express');
var helmet = require('helmet');
var fs = require('fs');
var path = require('path');
var favicon = require('static-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var session = require('express-session');
var memoryStore = session.MemoryStore;
var app = express();
// set variables
var options =
key: fs.readFileSync('./openssl_keys/server_key.pem'),
cert: fs.readFileSync('./openssl_keys/server_cert.pem')
;
var cookieSecret = "secret phrase";
var sessionStore = new memoryStore();
app.set('env', process.env.NODE_ENV || 'development');
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(favicon());
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded());
app.use(cookieParser(cookieSecret));
app.use(session(
secret: cookieSecret,
cookie: httpOnly: true, secure: true,
store: sessionStore
));
app.use(function(req, res, next)
res.locals.session = req.session;
next();
);
app.use(express.static(path.join(__dirname, 'public')));
//routes
require('./routes/index')(app);
require('./routes/test')(app);
// starting http and https servers
var http = require('http').createServer(app).listen(8000, function()
console.log("http server listening on port 8000");
);
var https = require('https').createServer(options, app).listen(8080, function()
console.log("https server listening on port 8080");
);
// starting socket.io & session handler
var serverIO = require('socket.io').listen(https);
var SessionSockets = require('session.socket.io-express4');
var io = new SessionSockets(serverIO, sessionStore, cookieParser);
io.on('connection', function(err, socket, session)
if(err) throw err;
console.log("connected");
//console.log(session);
socket.on('clientMessage', function(content)
console.log("received client message")
console.log(content);
);
);
module.exports = app;
我尝试了多种可能性,例如:
禁用https
服务器。
使用密码短语设置cookieParser
对象(因此它“实际上”将密码短语导出到io = new SessionSockets(serverIO, sessionStore, cookieParser);
)
使用最少的cookie
选项。
无论如何我对此有点迷茫,欢迎任何建议/批评。
更新
好的,经过多次尝试,我想我可以让它工作!
问题在于 cookieParser 初始化,正确的方法似乎是:
var cookieParser = require('cookie-parser');
app.use(cookieParser());
app.use(session(
secret: "secret phrase",
cookie: httpOnly: true, secure: true,
store: sessionStore
));
var io = new SessionSockets(serverIO, sessionStore, cookieParser());
请注意,如果我使用 var io = new SessionSockets(serverIO, sessionStore, cookieParser);
(而不是 cookieParser()
),则它不起作用。 这似乎是问题所在。
如果我使用:
app.use(cookieParser("secret phrase"));
app.use(session(
secret: "secret phrase",
cookie: httpOnly: true, secure: true,
store: sessionStore
));
var io = new SessionSockets(serverIO, sessionStore, cookieParser("secret phrase"));
然后模块崩溃并显示以下错误消息:
session.socket.io-express4/session.socket.io.js:41
ake.signedCookies[key] = handshake.signedCookies[key].match(/\:(.*)\./).pop();
^
TypeError: Cannot call method 'pop' of null
但如果我使用:
app.use(cookieParser("secret phrase"));
app.use(session(
secret: "secret phrase",
cookie: httpOnly: true, secure: true,
store: sessionStore
));
var io = new SessionSockets(serverIO, sessionStore, cookieParser());
然后一切看起来都很好。
现在,在 cookie-parser 文档 (https://github.com/expressjs/cookie-parser) 中,您可以传递一个密钥来对 cookie 进行签名。这是我想要的。
有人可以解释一下 cookie-parser 密码短语和会话密码短语的关系吗?它们必须相同/不同吗?
【问题讨论】:
另请参阅此相关答案:***.com/a/24859515/1407170 【参考方案1】:这是我针对以下环境的解决方案:
快递4.2.0 socket.io 1.1.0 cookie 解析器 1.0.1 cookie 会话 1.0.2代码:
var cookieParser = require('cookie-parser')();
var session = require('cookie-session')( secret: 'secret' ;
...
app.use(cookieParser);
app.use(session);
...
io.use(function(socket, next)
var req = socket.handshake;
var res = ;
cookieParser(req, res, function(err)
if (err) return next(err);
session(req, res, next);
);
);
然后你可以通过套接字的握手访问会话:
io.on('connection', function (socket)
console.log("Session: ", socket.handshake.session);
);
对于想知道它如何/为什么起作用的人:
我们通过 cookie 解析器发送handshake
请求,以便 cookie 可用
然后我们通过会话中间件发送handshake
,就好像它是一个正常的请求一样
中间件将session
附加到请求中
我们使用handshake
是因为出于所有意图和目的,这是一个正常的请求,解析器和会话中间件可以正确处理它。这就是为什么您必须通过handshake
访问session
【讨论】:
@Sean Adkinson 我不知道如何写入 cookie,你能帮忙吗?如果我将数据保存到 socket.handshake.session 它不会持续存在。 @youbetternot 我不是 100% 确定,因为我没有尝试过,但是使用 websocket 连接,您可能无法更新会话 cookie,因为浏览器是'不会读取它接收到的 websocket 消息的 Set-Cookie 标头。可能只有在最初的握手时它才会通过正常过程。如果你使用 server-cookies,你可能会有更好的运气,因为会话信息存储在服务器上,你可以在那里更新它。有意义吗? @SeanAdkinson 好的,感谢您的重播,我仍在努力寻找正确的解决方案。【参考方案2】:express 4.13.4 / socket.io 1.4.5
我浏览了所有解决方案和模块,但它们都不能在我的应用程序中运行。 最后——
app.use(session(
secret: COOKIE_SECRET,
resave: true,
saveUninitialized: true,
store:sessionStore,
cookie: domain: 'localhost',secure: false
));
io.use(function(socket, next)
session(
secret: COOKIE_SECRET,
resave: true,
saveUninitialized: true,
store:sessionStore,
cookie: domain: 'localhost',secure: false
)(socket.handshake, , next);
);
像魅力一样工作。
【讨论】:
?不幸的是,它会导致 MaxListenersExceededWarning:Possible EventEmitter memory leak detected nodejs 错误。【参考方案3】:express-socket.io-session 是针对您的问题的现成解决方案。通常在 socket.io 端创建的会话与在 express.js 中创建的会话具有不同的 sid
在知道这个事实之前,当我通过它寻找解决方案时,我发现了一些奇怪的东西。从 express.js 实例创建的会话可以在 socket.io 端访问,但相反的情况是不可能的。很快我就知道我必须通过管理 sid 来解决这个问题。但是,已经编写了一个包来解决这个问题。它有据可查,可以完成工作。希望对你有帮助
【讨论】:
【参考方案4】:使用新的 express-session 中间件,您只需添加 IO 中间件:
io.use(function(socket, next)
session(socket.handshake, , next);
);
一个完整的例子如下所示:
var io = require('socket.io')(server);
var Session = require('express-session'),
SessionStore = require('session-file-store')(Session);
session = Session(
store: new SessionStore( path: './tmp/sessions' ),
secret: 'pass',
resave: true,
saveUninitialized: true
);
io.use(function(socket, next)
session(socket.handshake, , next);
);
io.on('connection', function(socket)
console.log('a user connected');
socket.emit('chat message', "UserID: " + socket.handshake.session.uid);
);
我创建了一个超级迷你 npm 包socket.io-express-session,它的工作原理与我上面解释的一样。
【讨论】:
这个解决方案效果好一点:***.com/a/53641007/630169【参考方案5】:这对我有用
快递4.9.0 express.io 1.1.13 连接-redis 2.1.0 快速会话 1.8.2我想要的是通过 redis 与前端和后端 API 共享会话。 不同的机器,共享相同的数据库。在前端打开页面时会创建会话并登录用户,然后 api 会根据请求查找登录的用户。
var cookieParser = require('cookie-parser')();
var session = require('express-session');
var RedisStore = require('connect-redis')(session);
var db = require('./db')(config);
var sessionStore = session(
store: new RedisStore( client: db ),
secret: SECRET,
cookie: secure: false
);
app.use(cookieParser);
app.use(sessionStore);
// attach sessions to pure websocket requests
app.io.use(function(req, next)
var res = ;
cookieParser(req, res, function(err)
if (err) return next(err);
sessionStore(req, res, next);
);
);
注意:我将 cookie.secure 设置为 false,因此我可以在本地不使用 https 进行测试。
【讨论】:
io.use() 接受一个套接字作为它的第一个参数。 cookieParser 和 sessionStore 的第一个参数应该是 socket.handshake。除此之外,您的解决方案似乎有效:) 那是我最初找到的代码,但它并不完全有效。不知道为什么。我可以试试,然后添加进去【参考方案6】:这让我很难找到正确的解决方案。这对我有用:
/*
Just to see, before my code :
var sessionStore = new mongoStore(
db: db.connection.db,
collection: config.sessionCollection
);
app.use(session(
secret: config.sessionSecret,
store: sessionStore
));
*/
io.use(function(socket, next)
var handshake = socket.handshake;
if (handshake.headers.cookie)
cookieParser(config.sessionSecret)(handshake, , function(err)
handshake.sessionID = handshake.signedCookies['connect.sid']; // <- 'connect.sid' > your key could be different, but this is the default
handshake.sessionStore = sessionStore;
handshake.sessionStore.get(handshake.sessionID, function(err, data)
if (err) return next(err);
if (!data) return next(new Error('Invalid Session'));
handshake.session = new session.Session(handshake, data);
next();
);
);
else
next(new Error('Missing Cookies'));
);
express 4.2.0 / socket.io 1.0.6
【讨论】:
【参考方案7】:这可能适用于 express 4 / socket.io 1.X 我抓住了这个代码表单https://github.com/turbonetix/bus.io/blob/master/demo/chat/app.js
io.use(function (socket, next)
var handshake = socket.handshake;
if (handshake.headers.cookie)
cookieParser()(handshake, , function (err)
handshake.sessionID = connect.utils.parseSignedCookie(handshake.cookies[config.session.key], config.session.secret);
handshake.sessionStore = config.session.store;
handshake.sessionStore.get(handshake.sessionID, function (err, data)
if (err) return next(err);
if (!data) return next(new Error('Invalid Session'));
handshake.session = new session.Session(handshake, data);
next();
);
);
else
next(new Error('Missing Cookies'));
);
【讨论】:
以上是关于socket.io 和 express 4 个会话的主要内容,如果未能解决你的问题,请参考以下文章
与 socket.io 一起使用时 express-session 未设置会话 cookie