node.js - ¿Cómo compartir sesiones con Socket.IO 1.xy Express 4.x?
session (6)
Ahora, la respuesta original aceptada tampoco me funciona. Igual que @ Rahil051, express-socket.io-session módulo express-socket.io-session , y todavía funciona. Este módulo usa el analizador de cookies, para analizar la identificación de sesión antes de ingresar al middleware de sesión expresa. Creo que es fascinante @pootzko, @Mustafa y la respuesta de @ Kosar.
Estoy usando estos módulos:
"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"
}
mira los datos en 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);
});
};
¿Cómo puedo compartir una sesión con Socket.io 1.0 y Express 4.x? Uso una Tienda Redis, pero creo que no debería importar. Sé que tengo que usar un middleware para mirar las cookies y buscar la sesión, pero no sé cómo. Busqué pero no pude encontrar ningún trabajo
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''));
}
});
Hace apenas un mes y medio resolví el mismo problema y luego escribí una extensa publicación de blog sobre este tema que va de la mano con una aplicación de demostración completamente operativa alojada en GitHub. La solución se basa en los módulos de nodo de express-session , cookie-parser y connect-redis para atar todo. Le permite acceder y modificar sesiones desde el contexto REST y Sockets, que es bastante útil.
Las dos partes cruciales son la configuración de middleware:
app.use(cookieParser(config.sessionSecret));
app.use(session({
store: redisStore,
key: config.sessionCookieKey,
secret: config.sessionSecret,
resave: true,
saveUninitialized: true
}));
... y la configuración del servidor 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();
});
});
});
Van de la mano con un módulo de sesión simple ServiceService que hice que te permite hacer algunas operaciones básicas con sesiones y ese código se ve así:
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);
}
};
Como hay más código para todo esto que esto (como inicializar módulos, trabajar con sockets y llamadas REST tanto en el lado del cliente como del servidor), no pegaré todo el código aquí, puede verlo en el GitHub. y puedes hacer lo que quieras con eso.
La solución es sorprendentemente simple. Simplemente no está muy bien documentado. También es posible utilizar el middleware de sesión exprés como middleware Socket.IO con un adaptador pequeño como este:
sio.use(function(socket, next) {
sessionMiddleware(socket.request, socket.request.res, next);
});
Aquí hay un ejemplo completo con Express 4.x, Socket.IO 1.xy 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);
Lo he resuelto un poco, pero no es perfecto. No admite cookies firmadas, etc. Utilicé la función getcookie de express-session expresa. La función modificada es la siguiente:
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);
Usando la respuesta de Bradley Lederholz, así es como lo hice funcionar para mí. Consulte la respuesta de Bradley Lederholz para obtener más explicaciones.
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);
npmjs.com/package/express-socket.io-session
es una solución preparada para su problema. Normalmente, la sesión creada en socket.io end tiene diferentes sid que los creados en express.js
Antes de saber ese hecho, cuando estaba trabajando para encontrar la solución, encontré algo un poco raro. Las sesiones creadas desde la instancia de express.js eran accesibles en el socket.io final, pero no era posible lo mismo por el contrario. Y pronto llegué a saber que tenía que trabajar para administrar el sid para resolver ese problema. Pero, ya había un paquete escrito para abordar ese problema. Está bien documentado y hace el trabajo. Espero eso ayude