2015-09-22 47 views
9

Tôi có một websocket chạy trên node.js 4.0 trên máy chủ 10.0.4.18 tại cổng 8020. Tôi đã triển khai websocket của mình bằng cách sử dụng socket.io v1.3, express và express-session.Làm thế nào để tạo một cái bắt tay giữa máy chủ web PHP và Socket.io trên máy chủ node.js?

Dự án Definition

tôi cần để có thể tạo ra một phiên trong socket.io cho mọi người dùng kết nối với nó từ một ứng dụng PHP. Sau khi người dùng gửi yêu cầu HTTP đầu tiên, tôi sẽ xác thực chúng bằng cách sử dụng một mã thông báo sẽ được chuyển từ PHP sang socket.io cùng với yêu cầu HTTP.

Sau khi người dùng được xác thực, tôi cần lưu một số dữ liệu cá nhân trong phiên socket.io để sử dụng lại sau này. Mỗi khi người dùng làm mới ứng dụng PHP, socket.io sẽ cần phải biết dữ liệu phiên đã được tạo.

Vấn đề

Mỗi lần load lại dùng/làm mới trang PHP "nơi anh/cô ấy được kết nối từ", dữ liệu phiên bị mất. Máy chủ không biết rằng kết nối thuộc về phiên XYZ được tạo trước đó.

Tôi không chắc chắn cách tạo một cái bắt tay giữa PHP và node.js trong đó hai máy chủ có thể trao đổi một dữ liệu duy nhất để kết nối phiên socket.io với.

trông rất chặt chẽ tại các vấn đề

Tôi mở liên kết này https://10.0.4.18:8020/set/MikeA trong trình duyệt của mình. "Điều này đã tạo một phiên cho tôi trực tiếp từ node.js trên mã tuyến"

Sau đó, tôi kết nối với websocket bằng cách sử dụng PHP, bây giờ tôi có thể thấy rằng phiên không có vấn đề gì! Tôi đã có thể mở nhiều tab trong trình duyệt và cùng một phiên ở đó như mong đợi.

Lý do nó hoạt động lần này là vì url https://10.0.4.18:8020/set/MikeA đã thiết lập phiên và gắn nó giữa trình duyệt của tôi và phiên và từ đó tôi có thể đọc/ghi phiên của mình từ socket.io sử dụng express-socket.io- gói phiên https://www.npmjs.com/package/express-socket.io-session.

Nhưng nếu tôi không tạo phiên sử dụng url theo cách thủ công, phiên sẽ chỉ tốt cho một lần tải trang. Và mỗi lần trang được tải lại phiên sẽ bị hủy như không bao giờ tồn tại!

Câu hỏi

tôi cần phải phát triển các hành vi tương tự khi kết nối đến WebSocket qua https://10.0.4.18:8020/set/MikeA khi kết nối từ socket.io.

Làm cách nào để thiết lập bắt tay giữa máy chủ PHP và socket.io nơi hai máy chủ có thể kết hợp dữ liệu phiên cho đúng người dùng mỗi khi trang PHP được tải lại hoặc tab của trình duyệt mới được mở?

Đây là mã WebSocket tôi

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

Dưới đây là làm thế nào tôi kết nối với WebSocket từ máy chủ 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

tôi không nghĩ rằng điều này là có thể vì khi trang được tải lại hoặc trình duyệt di chuyển đến một trang khác tất cả các trường hợp JavaScript chết vì chúng là duy nhất trên mỗi trang và cần thiết lập kết nối ổ cắm mới, nếu bạn muốn xác định người dùng từ kết nối cách tốt nhất là làm việc với các phiên, khi người dùng đăng nhập hoặc kết nối lần đầu tiên, một id phiên duy nhất là c reated và được gửi từ máy chủ đến người dùng và được lưu trữ cục bộ, vì vậy mỗi khi khách hàng bắt đầu một kết nối mới, nó sẽ tự xác định nó với id phiên tới máy chủ và máy chủ nên recoginze –

+0

@DanielMendel Làm cách nào để chuyển lại phiên khách hàng? Từ máy chủ node.js, làm cách nào để tải một sessionID cụ thể sau yêu cầu đầu tiên? –

Trả lời

2

Store thẻ trong trình duyệt và gửi nó cùng với tất cả các yêu cầu của php , với một người mang ủy quyền trông giống như:

Authorization: Bearer token 

Vì vậy, mỗi lần máy chủ node.js nhận được là một yêu cầu bạn có thể so sánh mã thông báo mà người dùng tương ứng và nhận dữ liệu.

Bạn có thể lưu trữ mối quan hệ giữa userid và mã thông báo với ngày hết hạn trong bảng và làm mới mã thông báo trên mỗi lần đăng nhập.

Ngoài ra nếu bạn không muốn sử dụng một bảng, bạn có thể lưu trữ dữ liệu trong thẻ bài, với JWT

Bạn có thể tìm thêm thông tin ở đây:

http://jwt.io/

+0

Tôi hoàn toàn đồng ý với Alex7, trên thực tế, trước đây tôi đã tham gia phiên php với nút socket.io và nó hoạt động tốt cho tôi, nhưng tôi đã có rất nhiều mã kết hợp với phiên php vật lý trong máy chủ .. và đó là là một vấn đề về khả năng mở rộng, một lý do khác là .. nếu bạn có thể viết các phản hồi phụ trợ chưa được tách trong ví dụ định dạng Json, bạn có thể hiển thị chế độ xem bằng cách sử dụng hiệu suất máy khách thay vì sử dụng máy chủ để gửi các phản hồi trong mọi yêu cầu ... hiệu suất và bạn cũng có thể gói nó trong ứng dụng nhanh hơn (như ionic, phonegap, phản ứng, vv ..) –

+0

@ Alex7 Cảm ơn bạn đã phản hồi. Nếu tôi hiểu bạn một cách chính xác, tôi cần phải có một đối tượng quan hệ trên node.js của tôi để giữ mối quan hệ giữa sessionID và mã thông báo được truyền trong phần đầu trang. Sau đó, trong middleware tôi thêm một kiểm tra, nếu mã thông báo đã tồn tại sau đó tải phiên hiện có. Nếu không, hãy tạo phiên mới và lưu nó trong đối tượng quan hệ. Nhưng, làm thế nào tôi sẽ ép buộc phiên giao dịch để tạo ra một sessionID mới? –

+0

@MikeA tôi nghĩ rằng bạn đang tìm kiếm Session.regenerate() mà bạn có thể tìm thấy ở đây https://github.com/expressjs/session#sessionregenerate. Nhưng đây là 2 kịch bản: Trước tiên, bạn tạo khóa hết hạn sau 1 giờ và bạn cần khôi phục để không giữ lại thông tin đăng nhập của người dùng, thứ hai bạn thực hiện phiên hết hạn sau 1 tuần khi người dùng phải đăng nhập lại và bạn không cần tái tạo, bạn chỉ cần tạo một phiên mới. Bạn có thể tạo một cơ chế để khi nâng cấp ứng dụng, bạn xóa đối tượng lưu trữ sessionID và mã thông báo và tất cả người dùng phải đăng nhập lại để bạn không có dữ liệu cũ. – Alex7

Các vấn đề liên quan