我需要创建一个websocket,我可以从不同的域连接到它(websocket在10.0.4.18:8020上运行,客户端将从10.0.4.160:443 Build 连接 . )

每个用户必须具有唯一的会话,以便在身份验证发生后能够重用他/她的用户数据 .

由于客户端“aka用户的浏览器”位于不同的主机上,因此我很难将会话与在页面重新加载事件上创建它的客户端保持联系!

我想到了解决这个问题的解决方法

  • 使用客户端的 XMLHttpRequest() 函数创建会话,然后将sessionID返回给客户端

  • 使用 localStorage 在用户的浏览器中保存sessionId

  • 每次用户连接到websocket时,都将sessionId传递给socket.io.

  • 然后Websocket接受sessionId并重新加载它以使其再次可用 .

为了消除会话固定攻击,我将添加更多验证步骤,以确保使用客户端的IP和代理数据不会劫持sessionId .

另外,我需要触发 setInterval() 方法,该方法每秒都会进行外部API调用并更新会话变量 .

Question

如何正确地重新加载会话数据,我可以自动保存变量,而无需直接使用store.get()加载会话数据并保存它们?

Here is what I have done

我使用文件系统创建了会话 . 在每个请求中,我必须使用 store.get() 方法加载会话存储,更新会话数据,然后保存它 . 但我遇到的问题是每次我想要更新会话,因为你可以看到我的下面的代码 .

这就是我所做的!

var app = require('express')(),
    https = require('https'),
    fs = require('fs'),
    session = require('express-session'),
    fileStore = require('session-file-store')(session),
    base64url = require('base64url'),
    bodyParser = require("body-parser");
    cookieParser = require("cookie-parser"),
    env = require('./modules/config');

var server = https.createServer(
    {
        key: fs.readFileSync('certs/key.pem'),
        cert: fs.readFileSync('certs/cert.pem')
    }, app).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 icwsReq = require('./modules/icws/request.js'),
    icwsConn = require('./modules/icws/connection.js'),
    icwsInter = require('./modules/icws/interactions.js'),
    sessionValidator = require('./modules/validator.js');

var icwsRequest = new icwsReq();
var sessionChecker = new sessionValidator();

var sessionStoreFile = new fileStore({path: './tmp/sessions'});

var sessionOptions = {
        store: sessionStoreFile,
        secret: env.session.secret,
        saveUninitialized: true,
        resave: false,
        cookie: {
            path: '/',
            httpOnly: true,
            maxAge: 60 * 60 * 1000,
            secure: true
        }
    };

app.use(session(sessionOptions)); // session support for the app

app.use(bodyParser.urlencoded({ extended: false }));  //allows to pupulate req.body in the REST/PUT post requests!


// Set access control headers on every express route.
app.use(function (req, res, next){
    res.setHeader('x-powered-by', 'Express');
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type');
    res.setHeader('Access-Control-Allow-Methods', 'GET, POST');
    next();
});


//Middleware for authorizing a user before establishing a connection
io.use(function(req, next) {

    var sessionID = req.handshake.query.token || '';
    var token = req.handshake.query.auth || '';

    var origin = req.handshake.headers.origin;
    var ip = req.request.socket.remoteAddress; 
    var userAgent =  req.handshake.headers['user-agent'];

    if(!sessionID || !token){
        console.log('No Session found with this token! ' + sessionID);
        return next(new Error('No Session found with this token!'));
    }

    sessionStoreFile.get(sessionID, function(err, session){

        // session updated
        if(err){
            console.log(err);
            return next(new Error(err));
        }

        if(!session){
            console.log('Session Could not be loaded');
            return next(new Error('Session Could not be loaded'));
        }

        if(    session.token != token
            || session.ip != ip
            || session.agent != userAgent
            || session.origin != origin
        ){

            session.token = null;
            session.ip = null;
            session.agent = null;
            session.origin = null;

            sessionStoreFile.set(sessionID, session);

            console.log('This session is invalid! Please sign in');
            return next(new Error('This session is invalid! Please sign in'));
        }

        next();

    });
});

io.on('connection', function (socket) {

    var sessionID = socket.handshake.query.token;

    //load the session with the ID sessionID
    sessionStoreFile.get(sessionID, function(err, session){

        //add the socket.id to the queue
        session.clients.push(socket.id);

        //Save the session data after adding the connection to the queue
        sessionStoreFile.set(sessionID, session, function(){

            //Get the current session data "including current socket.id"
            sessionStoreFile.get(sessionID, function(err, session){

                //get an instance of icws connector
                icwsRequest.setConnection(session.icwsHost, session.icwsPort);

                var interactions = new icwsInter(icwsRequest);

                //Call the API everysecond, update the session then save the session
                setInterval(function(){
                    sessionStoreFile.get(sessionID, function(err, session){
                        //call the API and return the new data
                        session.queue = interactions.updateQueue();
                        //save the new data every second
                        sessionStoreFile.set(sessionID, session);
                    }

                }, 1000);

                //handle ICWS interactions
                socket.on('interaction', function(data){

                    var  task = data.task || '',
                         phone = data.phone || '',
                         interactionId = data.interactionId || '',
                         queueName = data.queueName || '';

                    //Place a phone call
                    if(task == 'call'){
                        interactions.call(phone);
                    }

                    //External transfer
                    if(task == 'eBlindTransfer'){
                        interactions.blindTransferCallExternal(interactionId, phone);
                    }           

                    //Internal Transfer
                    if(task == 'iBlindTransfer'){
                        interactions.blindTransferCallInternal(interactionId, queueName);
                    }                   

                });

                //send a chat message to all browser's tabs associated with the currect session
                socket.on('chat', function(msg){

                    var clients = session.clients;

                    console.log(clients);

                    for (var i in clients) {

                        var socketId = clients[i];
                        console.log('Client Said: ' + msg + ' socket Id: ' + socketId);
                        io.to(socketId).emit('chat', {message: 'Server Said: ' + msg});
                    }

                });

                //handle disconnect
                socket.on('disconnect', function(msg){

                    var index = session.clients.indexOf(socket.id);

                    if(index > -1){
                        session.clients.splice(index, 1);

                        //save session after removing a client
                        sessionStoreFile.set(sessionID, session, function(error){

                            if(!error){
                                console.log('Closing tabs: ' + socket.id);
                                console.log(session);
                            }
                        });
                    }
                });

                //handle errors
                socket.on('error', function(msg){
                    console.log('Error Message: ' + msg);
                }); 

            });

        });

    });

});


app.get('/', function (req, res) {
    res.send('welcome: ' + req.sessionID);
});


app.get('/handshake/:token', function (req, res) {

    var origin = req.headers.origin;
    var ip =  req.connection.remoteAddress;
    var userAgent =  req.headers['user-agent'];

    if(!req.params || !req.params.token || !ip || !origin || !userAgent){
        console.log('Missing Request!');
        return false;
    }

    if(!originIsAllowed(origin)){
        console.log('This is a cross-domain attack!');
        return false;
    }

    req.session.token = req.params.token;
    req.session.ip = ip;
    req.session.origin = origin; 
    req.session.agent = userAgent;
    req.session.clients = [];

    req.session.save(function(err){

        if (err) {
            connectionError(res, session);
        } else {
            res.json({
                token: req.sessionID
            });
        }

    });
});



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;
};