1

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

每个用户必须有一个唯一的会话才能在身份验证发生后重用他/她的用户数据。

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

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

  1. 使用来自客户端的函数创建会话XMLHttpRequest(),然后返回一个 sessionID 给客户端
  2. 使用将 sessionId 保存在用户的浏览器中localStorage
  3. 每次用户连接到 websocket 时,将 sessionId 传递给 socket.io。
  4. Websocket 然后获取 sessionId 并重新加载它以使其再次可用。

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

此外,我需要触发一个setInterval()方法,该方法将每秒调用外部 API 并更新会话变量。

问题

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

这是我所做的

我使用文件系统创建了会话。对于每个请求,我都必须使用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;
};
4

0 回答 0