如何与Socket.io 1.0和Express 4.x共享会话?我使用Redis商店,但我认为这无关紧要。我知道我必须使用中间件来查看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'));
}
});
答案 0 :(得分:185)
解决方案非常简单。它没有很好的记录。也可以使用快速会话中间件作为Socket.IO中间件,使用这样的小型适配器:
sio.use(function(socket, next) {
sessionMiddleware(socket.request, socket.request.res, next);
});
以下是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);
答案 1 :(得分:5)
仅仅一个半月前,我处理了同样的问题,然后在这个主题上写了一篇广泛的blog post,与GitHub上托管的全面工作demo app一起。该解决方案依赖于express-session,cookie-parser和connect-redis节点模块来捆绑所有内容。它允许您从REST和套接字上下文访问和修改会话,这非常有用。
两个关键部分是中间件设置:
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();
});
});
});
它们与我制作的简单会话服务模块一起使用,它允许您对会话执行一些基本操作,代码如下所示:
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上查看它,你可以随心所欲地做任何事情。
答案 2 :(得分:3)
是针对您的问题的现成解决方案。通常,在socket.io end创建的会话与在express.js
中创建的会话具有不同的sid在了解这个事实之前,当我通过它来寻找解决方案时,我发现了一些有点奇怪的东西。从express.js实例创建的会话可以在socket.io端访问,但相反的情况是不可能的。很快我就知道我必须通过管理sid来解决这个问题。但是,已经有一个包来解决这个问题。它有很好的文档记录,可以完成工作。希望它有所帮助
答案 3 :(得分:2)
使用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);
答案 4 :(得分:1)
PostgreSQL 工作示例 & 解决获取“会话信息为空且只有 cookie 的对象”的问题:
服务器端(Node.js + PostgreSQL):
const express = require("express");
const Server = require("http").Server;
const session = require("express-session");
const pg = require('pg');
const expressSession = require('express-session');
const pgSession = require('connect-pg-simple')(expressSession);
const PORT = process.env.PORT || 5000;
const pgPool = new pg.Pool({
user : 'user',
password : 'pass',
database : 'DB',
host : '127.0.0.1',
connectionTimeoutMillis : 5000,
idleTimeoutMillis : 30000
});
const app = express();
var ioServer = require('http').createServer(app);
var io = require('socket.io')(ioServer);
var sessionMiddleware = session({
store: new RedisStore({}), // XXX redis server config
secret: "keyboard cat",
});
io.use(function(socket, next) {
session(socket.request, {}, next);
});
app.use(session);
io.on("connection", socket => {
const ioSession = socket.request.session;
socket.on('userJoined', (data) => {
console.log('---ioSession---', ioSession)
}
}
客户端(反应原生应用): 要解决获取“空会话对象”的问题,您需要添加 withCredentials: true
this.socket = io(`http://${ip}:5000`, {
withCredentials: true,
});
答案 5 :(得分:0)
我有点解决了它,但它并不完美。不支持签名的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 :(得分:0)
现在,原来接受的答案对我来说也不起作用。与@ Rahil051相同,我使用了express-socket.io-session模块,它仍然有效。该模块使用cookie-parser,在进入快速会话中间件之前解析会话ID。 我认为@pootzko,@ Mustafa和@Kasar的回答是个傻瓜。
我正在使用这些模块:
"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);
});
};