我使用Socket.io设置了一个websocket,并在node.js服务器上表达了4个框架。
我正在尝试在使用websocket时为我的用户实施授权步骤。
当用户连接时,令牌将作为查询值传递给服务器。在服务器级别,我在数据库中查询与传递的令牌匹配的会话。如果找到一个会话,我会做很少的其他检查以确保令牌没有被劫持。
问题
会话数据似乎在每次重新加载页面时都被清除。或者服务器无法将sessionId链接到创建它的用户,因此每次生成新会话时都会这样。
我很困惑如何访问会话变量"如果已设置。"
我的代码问题
当用户重新加载他/她的页面/客户端时,会话数据将在新请求中变为未定义。会话很好,直到页面刷新,这是我的问题。即使用户刷新页面,我也需要能够保持会话活动。
问题
如何确保在每次刷新页面时都不会清除会话数据?
这是我的授权码
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后,其最后访问的值为12/31/1969 4:00:00 PM
此外,此cookie会在每次重新加载页面时发生变化。
在下面的@Osk建议之后这是我的新代码,它仍然没有在页面重新加载时保存我的会话数据。
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;
}
答案 0 :(得分:3)
您使用的是哪个版本的socket.io?
express-socket.io-session 适用于socket.io 1.x
我看到你在socket.io 1.x
上调用了io.set()
有关详细信息,请查看标识身份验证差异下的http://socket.io/docs/migrating-from-0-9/。 在那里,它说明了
旧的
io.set()
和io.get()
方法仅供弃用 支持向后兼容性。“
这可能与您的问题有关吗?
安装express-socket.io-session软件包时,软件包中有一个示例目录。它可能会派上用场来测试这个模块的工作示例。
答案 1 :(得分:1)
以下是如何在express和socket.io之间共享会话的工作示例,即使它们不在同一个域中也是如此。
(您可以在这里找到一个稍微不同的git存储库,其中包含一个运行示例https://github.com/dievardump/express-socket-auth)
我只是使用express-session
,我不明白为什么要使用其他中间件,因为它与socket.io完美配合
由于我没有redis
可访问,因此我使用require('session-file-store')
进行共享会话。
<强>问题强>
问题来自cross-domain
政策,该政策不允许您分享connect.sid
Cookie值。
解决方法是:
从主机提供非httpOnly会话Cookie(此处为我server.dev
)。 [express.js第16行]
通过JavaScript阅读,并在连接到connect.sid
[client.js第26:30行] 时将sessionId
值作为socket.io
参数发送
握手时,在使用会话中间件 [socket.js line 32:37] 读取握手之前,将connect.sid=socket.handshake.query.sessionId
的值添加到socket.handshake.headers.cookie
p>
<强>建筑强>
接下来是:
需要
的server.js express.js:通过http://server.dev:3000
投放HTML
在加载页面时创建会话
socket.js:通过http://socket.dev:8000
client.js
在http://server.dev:3000
连接到http://socket.dev:8000
<强>测试强>
我在这里使用的测试步骤:
客户端打开页面
如果未设置Cookie密钥connect.sid
客户端尝试连接Socket.io
:连接错误:[未通过身份验证]
客户来电/authenticate
客户端尝试连接Socket.io
,其值connect.sid
为sessionId
参数:Connection sucessfull
如果设置了Cookie connect.sid
Socket.io
,其值connect.sid
为sessionId
参数:Connection 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