2015-09-22 7 views
9

サーバー10.0.4.18のnode.js 4.0でポート8020で実行されているwebsocketを持っています。私はsocket.io v1.3、express、およびexpress-sessionを使用してwebsocketを実装しました。node.jsサーバー上のPHP WebサーバーとSocket.ioとの間にハンドシェイクを作成する方法は?

プロジェクト定義

私は、PHPアプリケーションから、それに接続するすべてのユーザーのためにsocket.ioでセッションを作成できるようにする必要があります。ユーザーが最初のHTTPリクエストを送信した後、HTTPリクエストとともにPHPからsocket.ioに渡されるトークンを使用してそれらを認証します。

ユーザーが認証された後、後で再利用するために、いくつかの個人データをsocket.ioセッション内に保存する必要があります。ユーザーがPHPアプリケーションをリフレッシュするたびに、socket.ioはすでに作成されているセッションデータを知る必要があります。

問題

たびにユーザーリロード/セッションデータが失われた、「彼/彼女はからの接続」PHPページをリフレッシュします。サーバーは、その接続が以前に作成されたセッションXYZに属していることを認識できません。

PHPとnode.jsの間でハンドシェークを作成する方法がわかりません。ここで、2つのサーバーがsocket.ioセッションを結び付ける固有のデータを交換できます。

問題で非常によく見

私は私のブラウザでこのリンクhttps://10.0.4.18:8020/set/MikeAを開きました。 「これでルートコードごとにnode.jsから直接セッションが作成されました」

その後、PHPを使用してwebsocketに接続しました。セッションが問題なくスティックされていることがわかりました。私はブラウザで複数のタブを開くことができ、同じセッションが期待通りにそこにあります。

URLがhttps://10.0.4.18:8020/set/MikeAであるため、私のブラウザとセッションの間にセッションが確立されているため、そこからexpress-socket.io-セッションパッケージhttps://www.npmjs.com/package/express-socket.io-session

しかし、私が手動でURLを使用してセッションを作成しないと、セッションは1ページの読み込みにのみ有効です。そして、ページが再ロードされるたびに、セッションは決して存在しないように破壊されます!

質問

socket.ioからhttps://10.0.4.18:8020/set/MikeA経由でのWebSocketへの接続接続するときとき私は同じ動作を開発する必要があります。

PHPページとページを再読み込みするたびに2人のサーバーがセッションデータを正しいユーザーに結びつけることができるPHPサーバーとsocket.ioの間でハンドシェイクを確立するにはどうすればよいですか?ここで

は、ここに私の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); 
}); 

である私は、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

私はしないでください:あなたはあなたがここでより多くの情報を見つけることができます

JWT

で、あなたは、トークンにデータを格納することができ、テーブルを使用しない場合も

ページがリロードされたり、ブラウザが別の場所に移動したときに、JavaScriptインスタンスがページごとに一意であり、新しいソケット接続が確立される必要があるため、JavaScriptインスタンスがすべて終了することが考えられます。接続は、セッションで作業するのが最善の方法です。ユーザーが初めてログインするか、初めて接続するときは、一意のセッションIDはcですサーバーからユーザーに送信され、ローカルに保存されるため、クライアントが新しい接続を開始するたびに、サーバーとのセッションIDでセッションが識別され、サーバーが再認識する必要があります。 –

+0

@DanielMendelセッションをどのように戻しますかクライアント? node.jsサーバーから、最初のリクエストの後に特定のsessionIDをロードするにはどうすればよいですか? –

答えて

2

ストアブラウザでトークンからのWebSocketに接続し、PHPからのすべての要求と一緒にそれを送る方法です

Authorization: Bearer token 

したがって、node.jsサーバーは次のようになります。あなたは、ユーザーが対応するトークンを比較し、データを取得することができます。

ユーザーIDと有効期限のあるトークンとの関係をテーブルに格納し、ログインごとにトークンを更新することができます。

http://jwt.io/

+0

私はAlex7と完全に同意しています。実際、以前はsocket.ioノードでphpセッションに参加していましたが、それは私にとってはうまくいっていますが、サーバーの物理PHPセッションに多くの結合コードが付いていました。スケーラビリティの問題です。もう1つの理由は..Json形式などの非結合バックエンド応答を書くことができれば、サーバーを使用してクライアントのパフォーマンスを使用してビューをレンダリングして、すべての要求で応答ビューを送信することができます。あなたはあなたのフィードバックに感謝します。 –

+0

@ Alex7あなたのフィードバックに感謝します。私が正しくあなたを理解していれば、セッションIDとヘッダーで渡されたトークンの間の関係を保持するnode.jsにリレーショナルオブジェクトを持たなければなりません。次に、ミドルウェアで、トークンがすでに存在する場合にチェックを追加し、既存のセッションをロードします。そうでない場合は、新しいセッションを生成し、関係オブジェクトに格納します。しかし、私はどのように新しいセッションIDを生成するためにexpress-sessionを強制しますか? –

+0

@MikeAここでは、https://github.com/expressjs/session#sessionregenerateで見つけることができるSession.regenerate()を探していると思います。しかし、ここでは2つのシナリオがあります:まず、1時間後にキーが期限切れになり、何度も何度もユーザーのログインを維持しないようにするために再生成が必要です。2回目のセッションの有効期限は1週間後です。再生成が必要な場合は、新しいセッションを作成するだけです。メカニズムを作成することで、アプリケーションをアップグレードするときにsessionIDとトークンを格納するオブジェクトを削除し、すべてのユーザーが再びログインして古いデータを持たないようにする必要があります。 – Alex7

関連する問題