如何在Socket.io/express-sessions中的授权事件上访问/保存会话数据?

我使用Socket.io设置websocket,并在node.js服务器上表示4个框架。

我正在尝试使用我的websocket时为我的用户执行授权步骤。

当用户连接时,令牌作为查询值传递给服务器。 在服务器级别,我查询数据库以查找与传递的令牌相匹配的会话。 如果find一个会话,我会做其他一些检查来确保令牌不被劫持。

问题

会话数据似乎在每个页面重新加载清除。 或者服务器无法将sessionId链接到创build它的用户,所以每次都会生成一个新的会话。

我很困惑如何访问会话variables“如果他们被设置”。

我的代码的问题

当用户重新加载他/她的页面/客户端时,会话数据将在新请求中变得不确定。 会话是好的,直到页面刷新这是我的问题。 即使在用户刷新页面之后,我也需要保持活动状态。

问题

如何确保会话数据不会在每个页面上被清除刷新?

这是我的授权码

io.set('authorization', function (handshakeData, accept) { var session = handshakeData.session || {}; //This is always undefined! console.log('Session Data:' + session.icwsSessionId); //var cookies = handshakeData.headers.cookie; var token = handshakeData._query.tokenId || ''; //console.log('Token: ' + token); if(!token){ console.log('Log: token was not found'); return accept('Token was found.', false); } //allow any user that is authorized if(session && session.autherized && token == session.token){ console.log('Log: you are good to go'); return accept('You are good to go', true); } //if the client changed their token "client logged out" //terminate the open session before opening a new one if (session.autherized && token != session.token){ var icwsConnection = new icwsConn(icwsRequest); icwsRequest.setConnection(session.icwsServer, session.icwsPort); icwsRequest.setIcwsHeaders(session.icwsSessionId, session.icwsToken); icwsConnection.logout(); session.autherized = false; session.token = null; session.icwsServer = null; session.icwsPort = null; session.icwsSessionId = null; session.icwsToken = null; icwsConnection = null; } 

这是我的整个代码,如果需要的话

 var env = require('./modules/config'), app = require('express')(), https = require('https'), fs = require('fs'), session = require('express-session'), redisStore = require("connect-redis")(session), sharedsession = require("express-socket.io-session"), base64url = require('base64url'); const server = https.createServer( { key: fs.readFileSync('certs/key.pem'), cert: fs.readFileSync('certs/cert.pem') }, function (req, res){ res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type'); } ).listen(env.socket.port, env.socket.host, function () { console.log('\033[2J'); console.log('Websocket is running at https://%s:%s', server.address().address, server.address().port); }); var io = require('socket.io')(server); const sessionMiddleware = session({ store: new redisStore({ host: env.redis.host, port: env.redis.port }), secret: env.session.secret, name: env.session.name, rolling: false, resave: true, saveUninitialized: true }); app.use(sessionMiddleware); // Use shared session middleware for socket.io // setting autoSave:true io.use(sharedsession(sessionMiddleware, { autoSave: true })); var icwsReq = require('./modules/icws/request.js'), icwsConn = require('./modules/icws/connection.js'), icwsInter = require('./modules/icws/interactions.js'), sessionValidator = require('./modules/validator.js'); var clients = {}; var icwsRequest = new icwsReq(); var sessionChecker = new sessionValidator(); app.get('/', function (req, res) { res.send('welcome'); }); io.set('authorization', function (handshakeData, accept) { var session = handshakeData.session || {}; //This is always undefined! console.log('Session Data:' + session.icwsSessionId); //var cookies = handshakeData.headers.cookie; var token = handshakeData._query.tokenId || ''; //console.log('Token: ' + token); if(!token){ console.log('Log: token was not found'); return accept('Token was found.', false); } //allow any user that is authorized if(session && session.autherized && token == session.token){ console.log('Log: you are good to go'); return accept('You are good to go', true); } /* if (!originIsAllowed(origin)) { // Make sure we only accept requests from an allowed origin socket.destroy(); console.log((new Date()) + ' Connection from origin ' + origin + ' rejected.'); return false; } */ //if the client changed their token "client logged out" //terminate the open session before opening a new one if (session.autherized && token != session.token){ var icwsConnection = new icwsConn(icwsRequest); icwsRequest.setConnection(session.icwsServer, session.icwsPort); icwsRequest.setIcwsHeaders(session.icwsSessionId, session.icwsToken); icwsConnection.logout(); session.autherized = false; session.token = null; session.icwsServer = null; session.icwsPort = null; session.icwsSessionId = null; session.icwsToken = null; icwsConnection = null; } var myIP = '10.0.4.195'; var decodedToken = base64url.decode(token); sessionChecker.validateData(decodedToken, myIP, env.session.duration, function(isValid, icws){ if(isValid){ session.authorized = true; session.icwsServer = icws.host; session.icwsPort = icws.port; session.token = token; session.icwsSessionId = null; session.icwsToken = null; icwsRequest.setConnection(icws.host, icws.port); var icwsConnection = new icwsConn(icwsRequest); icwsConnection.login(icws.username, icws.password, function(isLogged, icwsSession, headers){ if(isLogged && icwsSession.sessionId && icwsSession.csrfToken){ //icwsConnection.setWorkstation(icws.workstaton); session.icwsSessionId = icwsSession.sessionId; session.icwsToken = icwsSession.csrfToken; icwsRequest.setIcwsHeaders(session.icwsSessionId, session.icwsToken); console.log('Log: new connection to ICWS! ' + session.icwsSessionId ); } }); console.log('Log: new connection to websocket!') return accept('New connection to websocket!', true); } else { console.log('Log: token could not be validated!'); return accept('Token could not be validated!', false); } }); }); io.on('connection', function (socket) { console.log('Authorized Session! Websocket id ready for action!'); //var origin = socket.request.headers.origin || ''; //var myIP = socket.request.socket.remoteAddress || ''; if(!socket.request.sessionID){ console.log('Missing Session ID'); return false; } var socketId = socket.id; var sessionID = socket.request.sessionID; //Add this socket to the user's connection if(userCons.indexOf(socketId) == -1){ userCons.push(socketId); } clients[sessionID] = userCons; console.log(clients); //display all connected clients socket.on('placeCall', function(msg){ icwsInter.call(method, uri, params, header, true); }); socket.on('chat', function(msg){ console.log('Chat Message: ' + msg); socket.emit('chat', { message: msg }); }); socket.on('disconnect', function(msg){ console.log('Closing sessionID: ' + sessionID); var userCons = clients[sessionID] || []; var index = userCons.indexOf(socketId); if(index > -1){ userCons.splice(index, 1); console.log('Removed Disconnect Message: ' + msg); } else { console.log('Disconnect Message: ' + msg); } }); socket.on('error', function(msg){ console.log('Error Message: ' + msg); }); }); function originIsAllowed(origin) { // put logic here to detect whether the specified origin is allowed. var allowed = env.session.allowedOrigins || [] if(allowed.indexOf(origin) >= 0){ return true; } return false; } 

编辑

io cookie会在每个请求中更改。 当一个io cookie被创build时,它将有一个最后访问值12/31/1969 4:00:00 PM

而且,这个cookie会在每一页重新加载。

在@Osk下面的build议这里是我的新代码仍然没有保存我的会话数据在页面重新加载。

 var env = require('./modules/config'), app = require('express')(), https = require('https'), fs = require('fs'), session = require('express-session'), redisStore = require("connect-redis")(session), sharedsession = require("express-socket.io-session"), base64url = require('base64url'), cookieParser = require("cookie-parser"); const server = https.createServer( { key: fs.readFileSync('certs/key.pem'), cert: fs.readFileSync('certs/cert.pem') }, function (req, res){ res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type'); } ).listen(env.socket.port, env.socket.host, function () { console.log('\033[2J'); console.log('Websocket is running at https://%s:%s', server.address().address, server.address().port); }); var io = require('socket.io')(server); var sessionStore = new redisStore({ host: env.redis.host, port: env.redis.port }); const sessionMiddleware = session({ store: sessionStore, secret: env.session.secret, name: env.session.name, rolling: true, resave: false, saveUninitialized: false, cookie: { maxAge: 60 * 60 * 1000 } }); app.use(sessionMiddleware); // Use shared session middleware for socket.io // setting autoSave:true io.use(sharedsession(sessionMiddleware, { autoSave: false })); var icwsReq = require('./modules/icws/request.js'), icwsConn = require('./modules/icws/connection.js'), icwsInter = require('./modules/icws/interactions.js'), sessionValidator = require('./modules/validator.js'); var clients = {}; var icwsRequest = new icwsReq(); var sessionChecker = new sessionValidator(); app.get('/', function (req, res) { res.send('welcome'); }); //Middleware for authorizing a user before establishing a connection io.use(function(socket, next) { var origin = socket.request.headers.origin || ''; if (!originIsAllowed(origin)) { // Make sure we only accept requests from an allowed origin socket.destroy(); console.log((new Date()) + ' Connection from origin ' + origin + ' rejected.'); return false; } var myIP = socket.request.socket.remoteAddress || ''; var token = socket.handshake.query.tokenId || ''; var session = socket.handshake.session || {}; //This should be defined on a reload console.log('IP Address: ' + myIP + ' SessionID: ' + socket.handshake.sessionID); if(!token){ console.log('Log: token was not found'); return next(new Error('Token not found')); } //allow any user that is authorized if(session && session.autherized && token == session.token){ console.log('Log: you are good to go'); return next(new Error('You are good to go')); } //if the client changed their token "client logged out" //terminate the open session before opening a new one if (session.autherized && token != session.token){ var icwsConnection = new icwsConn(icwsRequest); icwsRequest.setConnection(session.icwsServer, session.icwsPort); icwsRequest.setIcwsHeaders(session.icwsSessionId, session.icwsToken); icwsConnection.logout(); session.autherized = false; session.token = null; session.icwsServer = null; session.icwsPort = null; session.icwsSessionId = null; session.icwsToken = null; icwsConnection = null; session.save(); } var decodedToken = base64url.decode(token); sessionChecker.validateData(decodedToken, myIP, env.session.duration, function(isValid, icws){ if(isValid){ session.authorized = true; session.icwsServer = icws.host; session.icwsPort = icws.port; session.token = token; session.icwsSessionId = null; session.icwsToken = null; icwsRequest.setConnection(icws.host, icws.port); var icwsConnection = new icwsConn(icwsRequest); /* icwsConnection.login(icws.username, icws.password, function(isLogged, icwsSession, headers){ if(isLogged && icwsSession.sessionId && icwsSession.csrfToken){ //icwsConnection.setWorkstation(icws.workstaton); session.icwsSessionId = icwsSession.sessionId; session.icwsToken = icwsSession.csrfToken; icwsRequest.setIcwsHeaders(session.icwsSessionId, session.icwsToken); console.log('Log: new connection to ICWS! ' + session.icwsSessionId ); } }); */ session.save(function(){ console.log('Log: new connection to websocket!'); }); return next(); } else { console.log('Log: token could not be validated!'); return next(new Error('Token could not be validated!')); } }); }); io.on('connection', function (socket) { console.log('Connection is validated and ready for action!'); var socketId = socket.id; if(!socket.handshake.sessionID){ console.log('sessionId was not found'); return false; } var sessionID = socket.handshake.sessionID; var userCons = clients[sessionID] || []; //Add this socket to the user's connection if(userCons.indexOf(socketId) == -1){ userCons.push(socketId); } clients[sessionID] = userCons; //console.log(clients); socket.on('placeCall', function(msg){ icws.call(method, uri, params, header, true); }); socket.on('chat', function(msg){ console.log('Chat Message: ' + msg); socket.emit('chat', { message: msg }); }); socket.on('disconnect', function(msg){ console.log('Closing sessionID: ' + sessionID); var userCons = clients[sessionID] || []; var index = userCons.indexOf(socketId); if(index > -1){ userCons.splice(index, 1); console.log('Removed Disconnect Message: ' + msg); } else { console.log('Disconnect Message: ' + msg); } }); socket.on('error', function(msg){ console.log('Error Message: ' + msg); }); }); function originIsAllowed(origin) { // put logic here to detect whether the specified origin is allowed. var allowed = env.session.allowedOrigins || [] if(allowed.indexOf(origin) >= 0){ return true; } return false; } 

你使用的是哪个版本的socket.io?

express-socket.io-session与socket.io 1.x一起使用

我看到你正在调用在socket.io 1.x上弃用的io.set()

有关这方面的更多信息,请查看http://socket.io/docs/migrating-from-0-9/标题下的身份validation差异。 在那里,这是说

旧的io.set()io.get()方法已被弃用,只有向后兼容才支持。

这可能与你的问题有关吗?

在安装express-socket.io-session软件包时,软件包内有一个示例目录。 testing这个模块的一个工作示例可能会派上用场。

下面是一个可以在express和socket.io之间共享会话的工作示例,即使它们不在同一个域中。

(你可以在https://github.com/dievard/express-socket-authfind一个稍微有点不同的git仓库)

我只是使用express-session ,我不明白为什么使用另一个中间件,因为它适用于socket.io

由于我没有redis可访问性,我使用require('session-file-store')作为共享会话。

问题

问题来自cross-domain策略,它不会让您分享connect.sid Cookie值。

解决方法是:

  • 提供来自主机的非httpOnly会话cookie(这里是我的server.dev )。 [express.js line 16]

  • 通过JavaScript读取并在连接到socket.io时发送connect.sid值作为sessionId参数[client.js line 26:30]

  • 在与会话中间件读取握手信息之前,握手时将connect.sid=socket.handshake.query.sessionId的值添加到socket.handshake.headers.cookie [socket.js line 32:37]

build筑

接下来是:

  • server.js需要

    • express.js:通过http://server.dev:3000在我的电脑上创build快速服务器

      • 提供HTML

      • 加载页面时创build会话

    • socket.js:通过http://socket.dev:8000创build在我的电脑上访问的Socket.io服务器

  • client.js

    • 服务于http://server.dev:3000

    • 连接到http://socket.dev:8000上的套接字服务器

testing

testing步骤我在这里使用:

  • 客户打开页面

  • 如果Cookie密钥connect.sid未设置

    • 客户端尝试连接到Socket.io :连接错误:[未validation]

    • 客户来电/authenticate

      • 会话生成
    • 客户端尝试使用connect.sid值作为sessionId参数连接到Socket.io sucessfull

  • 如果Cookie connect.sid被设置

    • 客户端尝试使用connect.sid值作为sessionId参数连接到Socket.io sucessfull

server.js

 require('./express'); require('./socket'); 

express.js

  var express = require('express'); var app = express(); var http = require('http'); var io = require('socket.io'); var bodyParser = require('body-parser'); var sessionExpress = require('express-session'); var FileStore = require('session-file-store')(sessionExpress); var secret = 'keyboard cat'; var session = sessionExpress({ secret: secret, store: new FileStore(), resave: true, saveUninitialized: true, cookie: { httpOnly: false, // important to allow client to read session cookie with JavaScript maxAge: 60 * 60 * 1000 } }); app.use(bodyParser.urlencoded({ extended: true })); app.use(express.static(__dirname)); app.use('/authenticate', session); app.get('/authenticate', function(req, res) { var session = req.session; session.userdata = session.userdata || {}; session.userdata.connected = true; session.save(function(err) { if (err) { connectionError(res, session); } else { res.status(200); res.send(); } }); }); // routes app.get('/', function(req, res) { res.send('welcome'); }); // setup servers var server = http.createServer(app); server.listen(3000); 

socket.js

 var express = require('express'); var app = express(); var http = require('http'); var io = require('socket.io'); var sessionExpress = require('express-session'); var FileStore = require('session-file-store')(sessionExpress); var secret = 'keyboard cat'; var sessionIdKey = 'connect.sid'; var session = sessionExpress({ secret: secret, store: new FileStore(), resave: true, saveUninitialized: true, cookie: { maxAge: 60 * 60 * 1000 } }); // setup servers var server = http.createServer(app, function (req, res){ res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type'); }); server.listen(8000); var sio = io(server); sio.use(function(socket, accept) { // writing sessionId, sent as parameter, on the socket.handshake cookies if (socket.handshake.query.sessionId) { var cookies = (socket.handshake.headers.cookie || '').split(';'); cookies.push(sessionIdKey + '=' + socket.handshake.query.sessionId); socket.handshake.headers.cookie = cookies.join(';'); } session(socket.handshake, {}, function(err) { if (err) return accept(err); console.log('User trying to connect to Socket.io'); var session = socket.handshake.session, userData = session.userdata || {}; // is connected and good if (!userData || !userData.connected) { console.log('----- User has no active session, error'); accept(new Error('Not authenticated')); } else { console.log('----- Socket.io connection attempt successful'); accept(null, session.userid !== null); } }); }); sio.on('connection', function (socket) { console.log('Connection'); }); 

client.js

  function getCookie(name) { var value = "; " + document.cookie; var parts = value.split("; " + name + "="); if (parts.length == 2) return decodeURIComponent(parts.pop().split(";").shift()); } function fetch(url, data, callback) { try { var x = new XMLHttpRequest(); x.open(data ? 'POST' : 'GET', url, 1); x.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); x.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); x.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); x.onreadystatechange = function () { x.readyState > 3 && callback && callback(x.responseText, x); }; x.send(data || null); } catch (e) { window.console && console.log(e); } }; function connectServer(cb) { var sessionId = getCookie('connect.sid'); var data = { forceNew: true, query : { } }; if (sessionId) { data.query.sessionId = sessionId } console.log('Trying to connect to Socket.io server'); var server = io('http://socket.dev:8000', data); server.on('error', function (err) { console.log('----- Connection error : [%s]', err); setTimeout(function () { cb(); }, 200); }); server.on('connect', function (data) { console.log('----- Connection successful with sessionId [%s]', sessionId); setTimeout(function () { cb(); }, 200); }); } if (getCookie('connect.sid')) { console.log('Session cookie Detected'); connectServer(function () { }); } else { connectServer(function () { console.log('Call ./authenticate to create session server side'); fetch('./authenticate', null, function () { console.log('Session created') connectServer(function () {}); }); }); } 

执行

第一页加载结果

客户:

 Trying to connect to Socket.io server ----- Connection error : [Not authenticated] Call ./authenticate to create session server side Session created Trying to connect to Socket.io server ----- Connection successful with sessionId [s:Ir9dVPi8wzplPCoeNXAsDlOkhL8AW0gx.wwzUQ2jftntWEc6lRdYqGxRBoszjPtjT4dBW/KjFIXQ] 

服务器:

 User trying to connect to Socket.io ----- User has no active session, error User trying to connect to Socket.io ----- Socket.io connection attempt successful Connection 

重新加载页面

客户:

 Session cookie Detected Trying to connect to Socket.io server ----- Connection successful with sessionId [s:Ir9dVPi8wzplPCoeNXAsDlOkhL8AW0gx.wwzUQ2jftntWEc6lRdYqGxRBoszjPtjT4dBW/KjFIXQ] 

服务器:

 User trying to connect to Socket.io ----- Socket.io connection attempt successful Connection