node.js 如何与 Socket.IO 1.x 和 Express 4.x 共享会话?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25532692/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to share sessions with Socket.IO 1.x and Express 4.x?
提问by Mustafa
How can I share a session with Socket.io 1.0 and Express 4.x? I use a Redis Store, but I believe it should not matter. I know I have to use a middleware to look at cookies and fetch session, but don't know how. I searched but could not find any working
如何与 Socket.io 1.0 和 Express 4.x 共享会话?我使用 Redis Store,但我认为这无关紧要。我知道我必须使用中间件来查看 cookie 和获取会话,但不知道如何。我搜索但找不到任何工作
var RedisStore = connectRedis(expressSession);
var session = expressSession({
store: new RedisStore({
client: redisClient
}),
secret: mysecret,
saveUninitialized: true,
resave: true
});
app.use(session);
io.use(function(socket, next) {
var handshake = socket.handshake;
if (handshake.headers.cookie) {
var str = handshake.headers.cookie;
next();
} else {
next(new Error('Missing Cookies'));
}
});
回答by Epeli
The solution is surprisingly simple. It's just not very well documented. It is possible to use the express session middleware as a Socket.IO middleware too with a small adapter like this:
解决方案出奇的简单。它只是没有很好的记录。也可以使用像这样的小适配器将 express 会话中间件用作 Socket.IO 中间件:
sio.use(function(socket, next) {
sessionMiddleware(socket.request, socket.request.res, next);
});
Here's a full example with express 4.x, Socket.IO 1.x and Redis:
这是一个使用 express 4.x、Socket.IO 1.x 和 Redis 的完整示例:
var express = require("express");
var Server = require("http").Server;
var session = require("express-session");
var RedisStore = require("connect-redis")(session);
var app = express();
var server = Server(app);
var sio = require("socket.io")(server);
var sessionMiddleware = session({
store: new RedisStore({}), // XXX redis server config
secret: "keyboard cat",
});
sio.use(function(socket, next) {
sessionMiddleware(socket.request, socket.request.res || {}, next);
});
app.use(sessionMiddleware);
app.get("/", function(req, res){
req.session // Session object in a normal request
});
sio.sockets.on("connection", function(socket) {
socket.request.session // Now it's available from Socket.IO sockets too! Win!
});
server.listen(8080);
回答by pootzko
Just a month and a half ago I dealt with the same problem and afterwards wrote an extensive blog poston this topic which goes together with a fully working demo apphosted on GitHub. The solution relies upon express-session, cookie-parserand connect-redisnode modules to tie everything up. It allows you to access and modify sessions from both the REST and Sockets context which is quite useful.
就在一个半月前,我处理了同样的问题,然后写了一篇关于这个主题的广泛的博客文章,与托管在 GitHub 上的一个完整的演示应用程序一起。该解决方案依赖于express-session、cookie-parser和connect-redis节点模块来绑定所有内容。它允许您从 REST 和 Sockets 上下文访问和修改会话,这非常有用。
The two crucial parts are middleware setup:
两个关键部分是中间件设置:
app.use(cookieParser(config.sessionSecret));
app.use(session({
store: redisStore,
key: config.sessionCookieKey,
secret: config.sessionSecret,
resave: true,
saveUninitialized: true
}));
...and SocketIO server setup:
...和 SocketIO 服务器设置:
ioServer.use(function (socket, next) {
var parseCookie = cookieParser(config.sessionSecret);
var handshake = socket.request;
parseCookie(handshake, null, function (err, data) {
sessionService.get(handshake, function (err, session) {
if (err)
next(new Error(err.message));
if (!session)
next(new Error("Not authorized"));
handshake.session = session;
next();
});
});
});
They go together with a simple sessionService module I made which allows you to do some basic operations with sessions and that code looks like this:
它们与我制作的一个简单的 sessionService 模块一起使用,该模块允许您对会话进行一些基本操作,代码如下所示:
var config = require('../config');
var redisClient = null;
var redisStore = null;
var self = module.exports = {
initializeRedis: function (client, store) {
redisClient = client;
redisStore = store;
},
getSessionId: function (handshake) {
return handshake.signedCookies[config.sessionCookieKey];
},
get: function (handshake, callback) {
var sessionId = self.getSessionId(handshake);
self.getSessionBySessionID(sessionId, function (err, session) {
if (err) callback(err);
if (callback != undefined)
callback(null, session);
});
},
getSessionBySessionID: function (sessionId, callback) {
redisStore.load(sessionId, function (err, session) {
if (err) callback(err);
if (callback != undefined)
callback(null, session);
});
},
getUserName: function (handshake, callback) {
self.get(handshake, function (err, session) {
if (err) callback(err);
if (session)
callback(null, session.userName);
else
callback(null);
});
},
updateSession: function (session, callback) {
try {
session.reload(function () {
session.touch().save();
callback(null, session);
});
}
catch (err) {
callback(err);
}
},
setSessionProperty: function (session, propertyName, propertyValue, callback) {
session[propertyName] = propertyValue;
self.updateSession(session, callback);
}
};
Since there is more code to the whole thing than this (like initializing modules, working with sockets and REST calls on both the client and the server side), I won't be pasting all the code here, you can view it on the GitHub and you can do whatever you want with it.
由于整个事情的代码比这更多(比如初始化模块,在客户端和服务器端使用套接字和 REST 调用),我不会在这里粘贴所有代码,你可以在 GitHub 上查看你可以用它做任何你想做的事。
回答by Rahil051
is a ready-made solution for your problem. Normally the session created at socket.io end has different sid than the ones created in express.js
是针对您的问题的现成解决方案。通常在 socket.io 端创建的会话与在 express.js 中创建的会话具有不同的 sid
Before knowing that fact, when I was working through it to find the solution, I found something a bit weird. The sessions created from express.js instance were accessible at the socket.io end, but the same was not possible for the opposite. And soon I came to know that I have to work my way through managing sid to resolve that problem. But, there was already a package written to tackle such issue. It's well documented and gets the job done. Hope it helps
在知道这个事实之前,当我努力寻找解决方案时,我发现了一些奇怪的东西。从 express.js 实例创建的会话可以在 socket.io 端访问,但相反的情况则不可能。很快我就知道我必须通过管理 sid 来解决这个问题。但是,已经有一个包可以解决这个问题。它有据可查,可以完成工作。希望能帮助到你
回答by Ali
Using Bradley Lederholz's answer, this is how I made it work for myself. Please refer to Bradley Lederholz's answer, for more explanation.
使用 Bradley Lederholz 的回答,这就是我让它为自己工作的方式。有关更多解释,请参阅 Bradley Lederholz 的回答。
var app = express();
var server = require('http').createServer(app);
var io = require('socket.io');
var cookieParse = require('cookie-parser')();
var passport = require('passport');
var passportInit = passport.initialize();
var passportSession = passport.session();
var session = require('express-session');
var mongoStore = require('connect-mongo')(session);
var mongoose = require('mongoose');
var sessionMiddleware = session({
secret: 'some secret',
key: 'express.sid',
resave: true,
httpOnly: true,
secure: true,
ephemeral: true,
saveUninitialized: true,
cookie: {},
store:new mongoStore({
mongooseConnection: mongoose.connection,
db: 'mydb'
});
});
app.use(sessionMiddleware);
io = io(server);
io.use(function(socket, next){
socket.client.request.originalUrl = socket.client.request.url;
cookieParse(socket.client.request, socket.client.request.res, next);
});
io.use(function(socket, next){
socket.client.request.originalUrl = socket.client.request.url;
sessionMiddleware(socket.client.request, socket.client.request.res, next);
});
io.use(function(socket, next){
passportInit(socket.client.request, socket.client.request.res, next);
});
io.use(function(socket, next){
passportSession(socket.client.request, socket.client.request.res, next);
});
io.on('connection', function(socket){
...
});
...
server.listen(8000);
回答by Pentatonic
Now, the original accepted answer doesn't work for me either. Same as @Rahil051, I used express-socket.io-sessionmodule, and it still works. This module uses cookie-parser, to parse session id before entering express-session middleware. I think it's silmiar to @pootzko, @Mustafa and @Kosar's answer.
现在,原来接受的答案对我也不起作用。与@Rahil051 相同,我使用了express-socket.io-session模块,它仍然有效。该模块使用 cookie-parser,在进入 express-session 中间件之前解析 session id。我认为@pootzko、@Mustafa 和@Kosar 的回答很相似。
I'm using these modules:
我正在使用这些模块:
"dependencies":
{
"debug": "^2.6.1",
"express": "^4.14.1",
"express-session": "^1.15.1",
"express-socket.io-session": "^1.3.2
"socket.io": "^1.7.3"
}
check out the data in socket.handshake:
查看socket.handshake中的数据:
const debug = require('debug')('ws');
const sharedsession = require('express-socket.io-session');
module.exports = (server, session) => {
const io = require('socket.io').listen(server);
let connections = [];
io.use(sharedsession(session, {
autoSave: true,
}));
io.use(function (socket, next) {
debug('check handshake %s', JSON.stringify(socket.handshake, null, 2));
debug('check headers %s', JSON.stringify(socket.request.headers));
debug('check socket.id %s', JSON.stringify(socket.id));
next();
});
io.sockets.on('connection', (socket) => {
connections.push(socket);
});
};
回答by Mustafa
I have kinda solved it, but it is not perfect. Does not support signed cookies etc. I used express-session's getcookie function. The modified function is as follows:
我已经解决了它,但它并不完美。不支持签名 cookie 等。我使用了express-session的 getcookie 函数。修改后的函数如下:
io.use(function(socket, next) {
var cookie = require("cookie");
var signature = require('cookie-signature');
var debug = function() {};
var deprecate = function() {};
function getcookie(req, name, secret) {
var header = req.headers.cookie;
var raw;
var val;
// read from cookie header
if (header) {
var cookies = cookie.parse(header);
raw = cookies[name];
if (raw) {
if (raw.substr(0, 2) === 's:') {
val = signature.unsign(raw.slice(2), secret);
if (val === false) {
debug('cookie signature invalid');
val = undefined;
}
} else {
debug('cookie unsigned')
}
}
}
// back-compat read from cookieParser() signedCookies data
if (!val && req.signedCookies) {
val = req.signedCookies[name];
if (val) {
deprecate('cookie should be available in req.headers.cookie');
}
}
// back-compat read from cookieParser() cookies data
if (!val && req.cookies) {
raw = req.cookies[name];
if (raw) {
if (raw.substr(0, 2) === 's:') {
val = signature.unsign(raw.slice(2), secret);
if (val) {
deprecate('cookie should be available in req.headers.cookie');
}
if (val === false) {
debug('cookie signature invalid');
val = undefined;
}
} else {
debug('cookie unsigned')
}
}
}
return val;
}
var handshake = socket.handshake;
if (handshake.headers.cookie) {
var req = {};
req.headers = {};
req.headers.cookie = handshake.headers.cookie;
var sessionId = getcookie(req, "connect.sid", mysecret);
console.log(sessionId);
myStore.get(sessionId, function(err, sess) {
console.log(err);
console.log(sess);
if (!sess) {
next(new Error("No session"));
} else {
console.log(sess);
socket.session = sess;
next();
}
});
} else {
next(new Error("Not even a cookie found"));
}
});
// Session backend config
var RedisStore = connectRedis(expressSession);
var myStore = new RedisStore({
client: redisClient
});
var session = expressSession({
store: myStore,
secret: mysecret,
saveUninitialized: true,
resave: true
});
app.use(session);

