How to share sessions with Socket.IO 1.x and Express 4.x?
如何与Socket.io 1.0和Express 4.x共享会话? 我使用Redis商店,但我认为这没有关系。 我知道我必须使用中间件来查看cookie和获取会话,但不知道如何。 我搜索了但找不到任何工作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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')); } }); |
解决方案非常简单。只是没有很好的记录。也可以通过如下所示的小型适配器将快速会话中间件用作Socket.IO中间件:
1 2 3 | sio.use(function(socket, next) { sessionMiddleware(socket.request, socket.request.res, next); }); |
这是Express 4.x,Socket.IO 1.x和Redis的完整示例:
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 30 | 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); |
就在一个半月前,我处理了同样的问题,然后写了一篇有关该主题的广泛博客文章,并与托管在GitHub上的完全正常的演示应用程序一起使用。该解决方案依赖于express-session,cookie解析器和connect-redis节点模块来捆绑一切。它允许您从REST和Sockets上下文访问和修改会话,这非常有用。
两个关键部分是中间件设置:
1 2 3 4 5 6 7 8 | app.use(cookieParser(config.sessionSecret)); app.use(session({ store: redisStore, key: config.sessionCookieKey, secret: config.sessionSecret, resave: true, saveUninitialized: true })); |
...和SocketIO服务器设置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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(); }); }); }); |
它们与我制作的简单sessionService模块一起使用,该模块允许您对会话执行一些基本操作,并且代码如下所示:
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | 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); } }; |
由于整个代码比这更多(例如初始化模块,在客户端和服务器端使用套接字和REST调用),因此我不会在此处粘贴所有代码,您可以在GitHub上查看您可以随心所欲地做任何事情。
express-socket.io会话
是针对您问题的现成解决方案。通常,在socket.io端创建的会话与在express.js中创建的会话具有不同的sid。
在不知道这个事实的情况下,当我努力寻找解决方案时,我发现了一些奇怪的东西。从express.js实例创建的会话可以在socket.io端进行访问,但是相反的情况则不可能。很快我就知道必须通过管理sid来解决该问题。但是,已经有一个解决该问题的软件包。有据可查,可以完成工作。希望能帮助到你
使用Bradley Lederholz的答案,这就是我使它自己工作的方式。请参阅Bradley Lederholz的答案,以获取更多说明。
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | 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); |
现在,最初接受的答案对我也不起作用。与@ Rahil051一样,我使用了express-socket.io-session模块,它仍然有效。此模块使用cookie解析器在进入Express-Session中间件之前解析会话ID。
我认为@ pootzko,@ Mustafa和@Kosar的答案是愚蠢的。
我正在使用以下模块:
1 2 3 4 5 6 7 8 | "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" } |
在socket.handshake中检查数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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); }); }; |
我已经解决了,但它并不完美。不支持签名的cookie等。我使用express-session的getcookie函数。修改后的功能如下:
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | 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); |