2015-09-22 11 views
9

Mam websocket działający na węźle node.js 4.0 na serwerze 10.0.4.18 na porcie 8020. Zaimplementowałem mój websocket przy użyciu socket.io v1.3, express i session-express.Jak utworzyć handshake pomiędzy serwerem PHP a serwerem Socket.io na serwerze node.js?

Definicja projektu

muszę być w stanie stworzyć sesję w socket.io dla każdego użytkownika, który łączy się to z aplikacji PHP. Po wysłaniu przez użytkownika pierwszego żądania HTTP, uwierzytelnię je za pomocą tokena, który zostanie przekazany z PHP do socket.io wraz z żądaniem HTTP.

Po uwierzytelnieniu użytkownika konieczne jest zapisanie niektórych danych osobistych w sesji socket.io w celu ich późniejszego użycia. Za każdym razem, gdy użytkownik odświeży aplikację PHP, socket.io będzie musiał znać dane sesji, które zostały już utworzone.

Problem

Za każdym razem, gdy użytkownik ładuje/odświeżyć stronę PHP „gdzie on/ona połączona z” dane sesji są tracone. Serwer nie wie, że połączenie należy do sesji XYZ, która została wcześniej utworzona.

Nie jestem pewien, jak utworzyć handshake między PHP i node.js, gdzie dwa serwery mogą wymieniać unikatowe dane, aby powiązać sesję socket.io z.

Bardzo blisko spojrzeć na problem

otworzyłem ten link https://10.0.4.18:8020/set/MikeA w przeglądarce. "To utworzyło dla mnie sesję bezpośrednio z pliku node.js na kod trasy"

Następnie połączyłem się z websemetlem za pomocą PHP, teraz widzę, że sesja nie ma problemu! Udało mi się otworzyć wiele kart w przeglądarce i ta sama sesja jest zgodna z oczekiwaniami.

Powodem, dla którego zadziałało tym razem jest to, że URL https://10.0.4.18:8020/set/MikeA ustanowił sesję i powiązał ją między moją przeglądarką a sesją i stamtąd mogłem odczytać/zapisać swoją sesję z socket.io przy użyciu funkcji express-socket.io- pakiet sesji https://www.npmjs.com/package/express-socket.io-session.

Jeśli jednak nie utworzę sesji przy użyciu adresu URL ręcznie, sesja będzie dobra tylko dla jednego obciążenia strony. Za każdym razem, gdy strona jest ponownie ładowana, sesja jest niszczona, jakby nigdy nie istniała!

Pytanie

muszę rozwijać takie samo zachowanie, gdy połączenie z websocket poprzez https://10.0.4.18:8020/set/MikeA podczas łączenia z socket.io.

Jak mogę ustanowić handshake pomiędzy serwerem PHP a gniazdem socket.io, w którym dwa serwery mogą powiązać dane sesji z właściwym użytkownikiem za każdym razem, gdy strona PHP zostanie ponownie załadowana lub otwarta zostanie nowa karta przeglądarki?

Oto mój kod 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); 
}); 

Oto jak połączyć się z websocket z serwera 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

nie wiem Myślę, że jest to możliwe, ponieważ po ponownym załadowaniu strony lub po przejściu przeglądarki do innej instancji wszystkie instancje kodu JavaScript umierają, ponieważ są unikalne na stronie i konieczne jest ustanowienie nowego połączenia z gniazdem, jeśli chcesz zidentyfikować użytkownika z nowego połączenie najlepszym sposobem jest praca z sesjami, kiedy użytkownik loguje się lub łączy po raz pierwszy, unikalny identyfikator sesji to c jest ponownie zapisywany i wysyłany z serwera do użytkownika i przechowywany lokalnie, więc za każdym razem, gdy klient rozpoczyna nowe połączenie, identyfikuje to samo z identyfikatorem sesji do serwera, a serwer powinien rozpoznać –

+0

@DanielMendel Jak przekażę sesję z powrotem do Klient? Z serwera node.js, jak załadowałbym konkretny identyfikator sesji po pierwszym żądaniu? –

Odpowiedz

2

Store token w przeglądarce i wysłać go wraz z każdym wnioskiem php , z nośnikiem autoryzacji, który wygląda następująco:

Authorization: Bearer token 

Za każdym razem serwer node.js otrzymuje jest to żądanie, w którym można porównać token, do którego koresponduje użytkownik i uzyskać dane.

Możesz przechowywać relację między identyfikatorem użytkownika i tokenem z datą wygaśnięcia w tabeli i odśwież tokena przy każdym logowaniu.

Także, jeśli nie chcesz korzystać z tabeli, można przechowywać dane w zasadzie, z JWT

można znaleźć więcej informacji tutaj:

http://jwt.io/

+0

Całkowicie zgadzam się z Alex7, w rzeczywistości wcześniej dołączyłem do sesji php z węzłem socket.io i działa dobrze, ale miałem dużo połączonego kodu dołączonego do fizycznej sesji php na serwerze ... i to jest jest problemem skalowalności, innym powodem jest .. jeśli możesz pisać niezwiązane odpowiedzi backendu w formacie Json, możesz renderować widok używając wydajności klienta zamiast używać serwera do wysyłania widoków odpowiedzi w każdym żądaniu ... i to polepszy się wydajność, a także możesz szybciej spakować ją w aplikacji (np. jonowej, fonegap, reagować itp.) –

+0

@ Alex7 Dziękujemy za opinię. Jeśli dobrze cię rozumiem, powinienem mieć obiekt relacyjny na moim węźle .js, który zachowuje relację między identyfikatorem sesji a tokenem przekazywanym w nagłówku. Następnie w oprogramowaniu pośredniczącym dodaję czek, jeśli token już istnieje, następnie wczytaj istniejącą sesję. Jeśli nie, wygeneruj nową sesję i zapisz ją w obiekcie relacji. Ale jak wymusić sesję ekspresową, aby wygenerować nową wartość sessionID? –

+0

@MikeA Myślę, że szukasz Session.regenerate(), które można znaleźć tutaj https://github.com/expressjs/session#sessionregenerate. Oto dwa scenariusze: Po pierwsze, klucz traci ważność po 1 godzinie i potrzebujesz regeneracji, aby nie utrzymywać logowania użytkownika ponownie, Po drugie, sesja wygasa po 1 tygodniu, kiedy użytkownik musi się ponownie zalogować, a Ty nie. potrzebujesz regeneracji, po prostu tworzysz nową sesję. Możesz utworzyć mechanizm, więc po aktualizacji aplikacji usuniesz obiekt przechowujący identyfikator sessionID i token, a wszyscy użytkownicy muszą się ponownie zalogować, aby nie mieć starych danych. – Alex7