2015-09-22 16 views
9

Ho una websocket in esecuzione su node.js 4.0 sul server 10.0.4.18 sulla porta 8020. Ho implementato il mio websocket utilizzando socket.io v1.3, express ed express-session.Come creare un handshake tra il web server PHP e Socket.io sul server node.js?

Progetto Definizione

ho bisogno di essere in grado di creare una sessione in socket.io per ogni utente che si connette ad esso da un'applicazione PHP. Dopo che l'utente ha inviato la prima richiesta HTTP, li autenticherò utilizzando un token che verrà passato da PHP a socket.io insieme alla richiesta HTTP.

Dopo l'autenticazione dell'utente, è necessario salvare alcuni dati personali all'interno della sessione socket.io per riutilizzarli in seguito. Ogni volta che l'utente aggiorna l'applicazione PHP, socket.io dovrà conoscere i dati della sessione che sono già stati creati.

Problema

Ogni volta che l'utente ricarica/rinfrescati la pagina PHP "dove lui/lei collegata da", i dati di sessione sono persi. Il server non riesce a sapere che la connessione appartiene alla sessione XYZ creata in precedenza.

Non sono sicuro di come creare un handshake tra PHP e node.js in cui i due server possono scambiare dati univoci per legare la sessione socket.io a.

Molto sguardo da vicino il problema

ho aperto questo link https://10.0.4.18:8020/set/MikeA nel mio browser. "Questo ha creato per me una sessione direttamente da node.js per il codice di instradamento"

Poi mi sono collegato alla websocket usando PHP, ora vedo che la sessione non si ferma! Sono stato in grado di aprire più schede nel browser e la stessa sessione è lì come previsto.

La ragione per cui ha funzionato questa volta è perché l'url https://10.0.4.18:8020/set/MikeA ha stabilito una sessione e l'ho legata tra il mio browser e la sessione e da lì ho potuto leggere/scrivere la mia sessione da socket.io usando express-socket.io- pacchetto di sessioni https://www.npmjs.com/package/express-socket.io-session.

Ma se non creo la sessione utilizzando l'url manualmente, la sessione sarà valida solo per il caricamento di una sola pagina. E ogni volta che la pagina viene ricaricata la sessione viene distrutta come se non fosse mai esistita!

Domanda

ho bisogno di sviluppare lo stesso comportamento quando il collegamento al websocket via https://10.0.4.18:8020/set/MikeA quando si collega dal socket.io.

Come posso stabilire una stretta di mano tra il server PHP e socket.io in cui i due server possono legare i dati della sessione all'utente corretto ogni volta che la pagina PHP viene ricaricata o viene aperta una nuova scheda del browser?

Ecco il mio codice websocket

var app = require('express')(), 
    https = require('https'), 
    fs = require('fs'), 
    session = require('express-session'), 
    sharedsession = require("express-socket.io-session"), 
    fileStore = require('session-file-store')(session), 
    base64url = require('base64url'), 
    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 clients = {}; 

var sessionOptions = { 
     store: sessionStoreFile, 
     secret: env.session.secret, 
     name: env.session.name, 
     rolling: true, 
     saveUninitialized: false, 
     resave: true, 
     unset: 'keep', 
     cookie: { 
      maxAge: 60 * 60 * 1000 
     } 
    }; 

var sessionMiddleware = session(sessionOptions); 

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


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


// Use shared session middleware for socket.io 
io.use(sharedsession(sessionMiddleware, { 
    autoSave: true 
})); 

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

    var myIP = socket.request.socket.remoteAddress || ''; 
    var token = socket.handshake.query.tokenId || ''; 
    var session = socket.handshake.session || {}; 

    if(!session && !token){ 
     console.log('Log: No session and no token!'); 
     return next(new Error('No tken/session found')); 
    } 

    if(!token){ 
     console.log('Log: token was not found'); 
     return next(new Error('Token not found')); 
    } 

    //SessionID should be defined on a page reload 
    console.log('IP Address: ' + myIP + '  SessionID: ' + socket.handshake.sessionID); 

    //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 decodedToken = base64url.decode(token); 

    sessionChecker.validateData(decodedToken, myIP, env.session.duration, function(isValid, icws){ 

     if(!isValid){ 
      console.log('Log: token could not be validated!'); 
      return next(new Error('Token could not be validated!')); 
     } 

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

     session.save(function(){ 
      console.log('Log: new connection to websocket!'); 
      return next(); 
     }); 
    }); 

}); 


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; 

    socket.on('chat', function(msg){ 

     for (var key in clients[sessionID]) { 
      if (clients[sessionID].hasOwnProperty(key)) { 
      var id = clients[sessionID][key]; 
      console.log('Client Said: ' + msg); 
      io.to(id).emit('chat', {message: 'Server Said: ' + 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); 
    }); 

}); 


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

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

app.get('/set/:name', function (req, res) { 
    req.session.name = req.params.name; 
    res.send('welcome: ' + req.session.name); 
}); 

Ecco come mi collego al websocket dal server PHP

<!doctype html> 
<html lang="en-US"> 
    <head> 
    <title>Socket.IO chat</title> 
    <meta charset="utf-8"> 
    <style> 
     * { margin: 0; padding: 0; box-sizing: border-box; } 
     body { font: 13px Helvetica, Arial; } 
     form { background: #000; padding: 3px; position: fixed; bottom: 0; width: 100%; } 
     form input { border: 0; padding: 10px; width: 90%; margin-right: .5%; } 
     form button { width: 9%; background: rgb(130, 224, 255); border: none; padding: 10px; } 
     #messages { list-style-type: none; margin: 0; padding: 0; } 
     #messages li { padding: 5px 10px; } 
     #messages li:nth-child(odd) { background: #eee; } 
    </style> 

    <script src="https://10.0.4.18:8020/socket.io/socket.io.js"></script> 
    <script type="text/javascript" src="/js/jquery-2.1.0.min.js"></script> 
    <script> 

     $(function(){ 
      var socket = io.connect('https://10.0.4.18:8020', {secure: true, port: 8020, query : 'PHP generated token that will be used to authenticate the user from node.js'}); 

      //When the "send" button is clicked 
      $('#f').click(function(e){ 
       e.preventDefault(); 
       var message = $('#m').val().trim(); 
       if(message == ''){ 
        return false; 
       } 

       socket.emit('chat', message); 
       $('#m').val(''); 
      }); 

      socket.on('chat', function(msg){ 
       $('#messages').append($('<li>').text(msg.message)); 
      }); 

     }); 

    </script> 

    </head> 
    <body> 
    <ul id="messages"></ul> 
    <form action="" id="f"> 
     <input id="m" autocomplete="off" /><button>Send</button> 
    </form> 
    </body> 
</html> 
+0

non lo faccio pensate che questo è possibile poiché quando la pagina viene ricaricata o i browser viaggiano a un'altra, tutte le istanze di JavaScript muoiono poiché sono univoci per pagina e occorre stabilire una nuova connessione socket, se si desidera identificare l'utente dal nuovo connessione il modo migliore è quello di lavorare con le sessioni, quando l'utente accede o si collega per la prima volta, un id di sessione univoco è c reated e inviato dal server all'utente e archiviato localmente, quindi ogni volta che il client avvia una nuova connessione, identifica sé stesso con l'id di sessione sul server e il server dovrebbe ricodificare –

+0

@DanielMendel Come potrei passare la sessione a il cliente? Dal server node.js, come caricare un sessionID specifico dopo la prima richiesta? –

risposta

2

Conservare il token nel browser e inviarlo insieme ad ogni richiesta del php , con un portatore di autorizzazione che assomiglia a:

Authorization: Bearer token 

Quindi ogni volta che il server node.js ottiene è una richiesta è possibile confrontare il token a cui corrisponde l'utente e ottenere i dati.

È possibile memorizzare la relazione tra l'ID utente e il token con una data di scadenza in una tabella e aggiornare il token ad ogni accesso.

anche se non si desidera utilizzare una tabella, è possibile memorizzare i dati nel token, con JWT

È possibile trovare maggiori informazioni qui:

http://jwt.io/

+0

Sono totalmente d'accordo con Alex7, infatti in precedenza ho partecipato alle sessioni di php con il nodo socket.io, e sta funzionando bene per me, ma ho avuto un sacco di codice accoppiato collegato a una sessione php fisica in un server .. e questo è è un problema di scalabilità, un altro motivo è .. se è possibile scrivere risposte backend disgiunte nel formato di JSON ad esempio, è possibile eseguire il rendering della vista utilizzando le prestazioni del client utilizzando invece il server per inviare le viste di risposta in ogni richiesta ... e migliorerà le prestazioni e anche tu puoi impacchettarle in app più velocemente (come ionico, phonegap, reagire ecc.) –

+0

@ Alex7 Grazie per il tuo feedback. Se ho capito bene, dovrei avere un oggetto relazionale sul mio node.js che mantiene una relazione tra sessionID e il token passato nell'intestazione. Quindi nel middleware aggiungo un controllo, se il token esiste già, quindi carica la sessione esistente. In caso contrario, generare una nuova sessione e memorizzarla nell'oggetto delle relazioni. Ma come potrei forzare express-session a generare un nuovo sessionID? –

+0

@MikeA penso che tu stia cercando Session.regenerate() che puoi trovare qui https://github.com/expressjs/session#sessionregenerate. Ma qui ci sono 2 scenari: in primo luogo, si fa scadere la chiave dopo 1 ora e si ha bisogno di rigenerazione per non mantenere l'accesso dell'utente ancora e ancora, in secondo luogo si fa scadere la sessione dopo 1 settimana quando l'utente deve accedere nuovamente e non si hai bisogno della rigenerazione, devi solo creare una nuova sessione. È possibile creare un meccanismo in modo che quando si aggiorna l'applicazione, si elimina l'oggetto che memorizza il sessionID e il token e tutti gli utenti devono effettuare nuovamente l'accesso in modo da non avere dati vecchi. – Alex7