Как обмениваться сеансами с Socket.IO 1.x и Express 4.x?
Как я могу поделиться сеансом с 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'));
}
});
Ответы
Ответ 1
Решение на удивление прост. Это просто не очень хорошо документировано. Возможно использовать промежуточное ПО экспресс-сессии в качестве промежуточного программного обеспечения Socket.IO также с небольшим адаптером, например:
sio.use(function(socket, next) {
sessionMiddleware(socket.request, socket.request.res, next);
});
Вот полный пример с выражением 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 available from Socket.IO sockets too! Win!
});
server.listen(8080);
Ответ 2
Всего полтора месяца назад я столкнулся с одной и той же проблемой, после чего написал обширный пост в блоге по этой теме, который сочетается с полностью работающее демонстрационное приложение размещенное на GitHub. Решение основывается на express-session, cookie-parser и connect-redis node модули, чтобы связать все. Он позволяет вам получать доступ и изменять сеансы из контекста REST и Sockets, что весьма полезно.
Две важные части - это установка промежуточного программного обеспечения:
app.use(cookieParser(config.sessionSecret));
app.use(session({
store: redisStore,
key: config.sessionCookieKey,
secret: config.sessionSecret,
resave: true,
saveUninitialized: true
}));
... и настройка сервера 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();
});
});
});
Они идут вместе с простым модулем 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);
}
};
Поскольку для всего этого существует больше кода (например, инициализация модулей, работа с сокетами и REST-вызовами как на стороне клиента, так и на стороне сервера), я не буду вставлять весь код здесь, вы можете просмотреть его на GitHub, и вы можете делать с ним все, что хотите.
Ответ 3
express-socket.io-session
- готовое решение для вашей проблемы. Обычно сеанс, созданный в socket.io end, имеет различный sid, чем те, что созданы в express.js
Прежде чем зная этот факт, когда я работал над этим, чтобы найти решение, я нашел что-то немного странное. Сессии, созданные из экземпляра express.js, были доступны в конце socket.io, но то же самое было невозможно для противоположного. И вскоре я узнал, что мне нужно проделать свой путь через управление sid, чтобы решить эту проблему. Но для решения этой проблемы уже был составлен пакет. Он хорошо документирован и выполняет свою работу. Надеюсь, что это поможет.
Ответ 4
Используя ответ Брэдли Ледерхольца, я сделал это для себя. Пожалуйста, обратитесь к ответу Брэдли Ледерхольца, чтобы узнать больше.
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);
Ответ 5
Я как-то решил, но это не идеально. Не поддерживает подписанные файлы 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);
Ответ 6
Теперь исходный принятый ответ тоже не работает для меня. То же, что и @Rahil051, я использовал express-socket.io-session модуль, и он все еще работает. Этот модуль использует cookie-парсер, чтобы проанализировать идентификатор сеанса до ввода промежуточного программного обеспечения экспресс-сессии.
Я думаю, что это смехотворение @pootzko, @Mustafa и @Kosar ответ.
Я использую эти модули:
"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:
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);
});
};