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



我在node.js服务器上使用Socket.io和express 4框架设置了一个websocket。

我正在尝试在使用我的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('33[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('33[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会话与socket.io 1.x 一起工作

我看到你在调用io.set(),它在socket.io 1.x 上已被弃用

有关更多信息,请查看http://socket.io/docs/migrating-from-0-9/在标题"身份验证差异"下。有人说

旧的io.set()io.get()方法已弃用,并且仅限于支持向后兼容性。">

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

安装express-socket.io-session包时,包中有一个示例目录。针对该模块的工作示例进行测试可能会派上用场。

下面是一个工作示例,说明如何在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读取并在连接到socket.io时将connect.sid值作为sessionId参数发送[client.js行26:30]

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

体系结构

下面是:

  • 需要的server.js

    • express.js:创建通过http://server.dev:3000 在我的计算机上访问的express服务器

      • 提供HTML

      • 加载页面时创建会话

    • socket.js:创建通过http://socket.dev:8000 在我的计算机上访问的socket.io服务器

  • client.js

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

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

测试

我在这里使用的测试步骤:

  • 客户端打开页面

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

    • 客户端尝试连接到Socket.io:连接错误:[未通过身份验证]

    • 客户端调用/authenticate

      • 生成会话
    • 客户端尝试连接到值为connect.sidSocket.io作为sessionId参数:连接成功完成

  • 如果cookie connect.sid设置为

    • 客户端尝试连接到值为connect.sidSocket.io作为sessionId参数:Connection successfull

文件

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

相关内容

  • 没有找到相关文章

最新更新