如何在 node.js 服务器上的 PHP Web 服务器和 Socket.io 之间创建握手?

     2023-03-07     33

关键词:

【中文标题】如何在 node.js 服务器上的 PHP Web 服务器和 Socket.io 之间创建握手?【英文标题】:How to create a handshake between PHP web server and Socket.io on node.js server? 【发布时间】:2015-12-20 03:23:18 【问题描述】:

我有一个 websocket 在服务器 10.0.4.18 的 8020 端口上的 node.js 4.0 上运行。我使用 socket.io v1.3、express 和 express-session 实现了我的 websocket。

项目定义

我需要能够在 socket.io 中为从 PHP 应用程序连接到它的每个用户创建一个会话。在用户发送第一个 HTTP 请求后,我将使用一个令牌对他们进行身份验证,该令牌将与 HTTP 请求一起从 PHP 传递到 socket.io。

用户通过身份验证后,我需要在 socket.io session 中保存一些个人数据以供以后重用。每次用户刷新 PHP 应用程序时,socket.io 都需要知道已经创建的会话数据。

问题

每次用户重新加载/刷新“他/她从哪里连接”的 PHP 页面时,会话数据都会丢失。服务器不知道该连接属于先前创建的会话 XYZ。

我不确定如何在 PHP 和 node.js 之间创建握手,两个服务器可以交换唯一数据以将 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 从 socket.io 读/写我的会话-会话包https://www.npmjs.com/package/express-socket.io-session.

但是,如果我不使用 url 手动创建会话,则会话仅适用于一页加载。每次重新加载页面时,会话都会被破坏,就像它从未存在过一样!

问题

从 socket.io 连接时,我需要在通过 https://10.0.4.18:8020/set/MikeA 连接到 websocket 时产生相同的行为。

我如何在 PHP 服务器和 socket.io 之间建立握手,每次重新加载 PHP 页面或打开新浏览器的选项卡时,两个服务器可以将会话数据绑定到正确的用户?

这是我的 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 服务器连接到 websocket 的方式

<!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>

【问题讨论】:

我认为这是不可能的,因为当页面重新加载或浏览器移动到另一个页面时,所有的 JavaScript 实例都会死掉,因为它们每个页面都是唯一的,并且需要一个新的套接字连接已建立,如果要从新连接中识别用户,最好的方法是使用会话,当用户第一次登录或连接时,会创建一个唯一的会话 ID,并将其从服务器发送给用户并存储在本地,因此每次客户端启动新连接时,它都会使用会话 ID 向服务器标识自己,并且服务器应该识别 @DanielMendel 如何将会话传递回客户端?从 node.js 服务器,我将如何在第一次请求后加载特定的 sessionID? 【参考方案1】:

将令牌存储在浏览器中,并与来自 php 的每个请求一起发送,并带有如下所示的授权承载:

Authorization: Bearer token

因此,每次 node.js 服务器获取请求时,您都可以比较用户对应的令牌并获取数据。

您可以将用户 ID 和令牌之间的关系存储在一个表中,并在每次登录时刷新令牌。

另外,如果你不想使用表,你可以将数据存储在令牌中,使用 JWT

您可以在此处找到更多信息:

http://jwt.io/

【讨论】:

我完全同意 Alex7,事实上,我之前加入了 socket.io 节点的 php 会话,它对我来说很好,但是我有很多耦合代码附加到物理 php 会话在服务器中..这是一个可伸缩性问题,另一个原因是..如果您可以以例如 Json 格式编写未耦合的后端响应,则可以使用客户端性能呈现视图,而不是使用服务器在每个请求中发送响应视图...它会提高性能,您也可以更快地将其打包到应用程序中(如 ionic ,phonegap ,react 等。) @Alex7 感谢您的反馈。如果我理解正确,我的 node.js 上应该有一个关系对象,它保持 sessionID 和标头中传递的令牌之间的关系。然后在中间件中添加一个检查,如果令牌已经存在,则加载现有会话。如果不是,则生成一个新会话并将其存储在关系对象中。但是,我将如何强制 express-session 生成新的 sessionID? @MikeA 我想你正在寻找 Session.regenerate() ,你可以在这里找到github.com/expressjs/session#sessionregenerate。但是这里有两种情况:首先,您使密钥在 1 小时后过期,并且您需要重新生成以不让用户一次又一次地登录,其次,您使会话在 1 周后过期,此时用户必须再次登录而您没有需要重新生成,您只需创建一个新会话。您可以创建一种机制,以便在升级应用程序时删除存储 sessionID 和令牌的对象,并且所有用户都必须重新登录,这样您就没有旧数据。 @Alex7,我尝试使用 regenerate() 但它不起作用。我收到此错误socket.session.regenerate(function(err) ^ TypeError: Cannot read property 'regenerate' of undefined at Array.&lt;anonymous&gt; @MikeA 我对节点不是很熟悉,但根据我从他们的文档中了解到的情况,我相信会话重新生成必须根据 req.session.regenerate 之类的请求进行。我不确定您的套接字是否与他们的示例中的 req 变量等效。也许您使用 req.session 生成会话并将其保存在 socket.session 中。所以要重新生成它,你必须做同样的事情。我不确定我是否可以进一步帮助您,希望对您有所帮助

如何将数据从 Heroku 上托管的 Node.js 应用程序发送到托管在完全独立(Cpanel)服务器上的 PHP 文件?

...ku上托管的Node.js应用程序发送到托管在完全独立(Cpanel)服务器上的PHP文件?【英文标题】:HowcanIsenddatafromaNode.jsapplicationhostedonHerokutoaPHPfilehostedonacompletelyseparate(Cpanel)server?【发布时间】:2021-05-1516:32:05【问题描述】:在搜索了3... 查看详情

在 vagrant 中将 Node.js 连接到 apache 服务器上的 postgres

】在vagrant中将Node.js连接到apache服务器上的postgres【英文标题】:ConnectingNode.jstopostgresonapacheserverinvagrant【发布时间】:2018-12-1011:55:27【问题描述】:各位程序员,您好。我正在为Web应用程序重建整个后端,并使用typescript从php后... 查看详情

即使在事件循环中没有要执行的回调,Node.js Web 服务器如何保持运行?

】即使在事件循环中没有要执行的回调,Node.jsWeb服务器如何保持运行?【英文标题】:HowdoesANode.jsWebServerKeepRunningEvenWhenThereAreNoCallbacksToBeExecutedInTheEventLoop?【发布时间】:2019-10-1809:06:13【问题描述】:下面的代码是一个简单的Nod... 查看详情

与 Apache 相比,Node.js 的性能如何?

】与Apache相比,Node.js的性能如何?【英文标题】:HowdoesNode.jsperformcomparedtoApache?【发布时间】:2011-03-2209:22:46【问题描述】:Node.js是否比Apache更快、更具可扩展性?是否有任何性能数据来支持Node.js在Apache上的Web应用程序的性能... 查看详情

如何在 Node.js 和 Express 上的同一个 .ejs 文件上呈现 MySQL 查询的多个结果?

】如何在Node.js和Express上的同一个.ejs文件上呈现MySQL查询的多个结果?【英文标题】:HowtorendermultipleresultfromMySQLqueryonthesame.ejsfileonNode.jsandExpress?【发布时间】:2013-06-1015:24:46【问题描述】:我现在正在学习Node.js并创建一个Web应... 查看详情

如何让 EC2 上的 Node.js 服务器永远运行?

】如何让EC2上的Node.js服务器永远运行?【英文标题】:HowdoIleaveNode.jsserveronEC2runningforever?【发布时间】:2014-12-0211:11:52【问题描述】:从我的问题可以看出,我是新手...我建立了我的第一个网站,我设置了我的第一个Node.js服务... 查看详情

Heroku上的Node.js Web Socket H15空闲连接超时

】Heroku上的Node.jsWebSocketH15空闲连接超时【英文标题】:Node.jsWebSocketH15IdleConnectiontimeoutonHeroku【发布时间】:2014-11-0207:39:24【问题描述】:我们在Heroku上运行一个Node.js和Express应用程序,该应用程序使用ws库来实现实时Web套接字。... 查看详情

在 Linux 服务器上的何处部署 node.js 应用程序?

】在Linux服务器上的何处部署node.js应用程序?【英文标题】:Wheretodeploynode.jsappsonaLinuxserver?【发布时间】:2012-06-0317:11:50【问题描述】:我在Linux服务器上运行4个独立的Node.js应用程序,具有不同的端口和前面的代理。由于我(... 查看详情

Node.js:如何使用 SOAP XML Web 服务

】Node.js:如何使用SOAPXMLWeb服务【英文标题】:Node.js:howtoconsumeSOAPXMLwebservice【发布时间】:2012-01-2903:03:13【问题描述】:我想知道使用node.js使用SOAPXMLWeb服务的最佳方式是什么谢谢!【问题讨论】:如果您使用node-soap并弄清楚如... 查看详情

如何从部署在 AWS Elastic beanstalk 上的 node.js express 应用程序获取客户端 IP?

...414:36:01【问题描述】:我正在使用elasticbeanstalk和ngnix代理服务器。我的应用程序代码在node.jsexp 查看详情

使用 Node.js/socket.io 和 php/mysql 的 Web 应用程序

...陌生。目前我有一个完成了一半的私人网络项目,它只在服务器端使用PHP和MySQL数据库运行。我决定使用socket.io将它提升到更高级的水平,以实 查看详情

如何杀死 node.js 上的打开进程?

】如何杀死node.js上的打开进程?【英文标题】:Howtokillanopenprocessonnode.js?【发布时间】:2012-07-0120:07:58【问题描述】:我正在尝试在sublime上为Node.js设置一个构建系统,所以我可以按F7在打开的文件上调用“节点”。问题是该进... 查看详情

如何将 PHP 用户详细信息(会话)解析到 Node.js 服务器?

】如何将PHP用户详细信息(会话)解析到Node.js服务器?【英文标题】:HowcanIparsePHPuserdetails(session)toNode.jsserver?【发布时间】:2014-12-0712:01:11【问题描述】:所以我有2页:在Node.js和Socket.io(WebSockets)上运行的聊天页面。以及用户登... 查看详情

如何在 AWS 上的 Amazon Linux AMI 中自动启动 node.js 应用程序?

】如何在AWS上的AmazonLinuxAMI中自动启动node.js应用程序?【英文标题】:HowcanIautomaticallystartanode.jsapplicationinAmazonLinuxAMIonaws?【发布时间】:2012-07-0119:35:45【问题描述】:是否有简要指南来说明如何在实例启动和运行时启动应用程序... 查看详情

node简介

1什么是Node.js?是一个可以让JavaScript运行在服务器端的平台。它可以让JavaScript脱离浏览器的束缚运行在服务器环境下,就像运行Python、Perl、PHP、Ruby程序一样。你可以用Node.js轻松地进行服务器端应用开发,Python、Perl、PHP、Ruby能... 查看详情

如何在node.js中拥有多个关联/条件/包含在findAll上的续集

】如何在node.js中拥有多个关联/条件/包含在findAll上的续集【英文标题】:howtohavemultipleassociations/conditions/includesinnode.jssequelizeonfindAll【发布时间】:2015-01-0618:17:10【问题描述】:我有一个mysql数据库,我正在通过node.js中的sequelize... 查看详情

Azure 上的 Node.js 远程调试

...的环境。似乎“mysite.azurewebsites.net/server.js/debug”连接到服务器并加载脚本,但Websocket连接返回。Overrides.js:17Web 查看详情

html站点上的Node.js mysql显示表

...ble)。但我真的不知道该怎么做。对于具有PHP背景的人,如何在nodejs中的HTML中显示 查看详情