PHP、nodeJS 和会话

Posted

技术标签:

【中文标题】PHP、nodeJS 和会话【英文标题】:PHP, nodeJS and sessions 【发布时间】:2014-08-09 09:04:49 【问题描述】:

我有一个传递 php 文件的经典 apache 服务器,以及一个用于在那个 PHP 网站上进行实时事件管理的 nodeJS 服务器(带有 socket.io,但没有 express/connect)。我有时需要对连接到 nodeJS 服务器的客户端进行身份验证,但是当用户重新加载页面时,此身份验证会丢失,因为它还会重新加载 socket.io 客户端(我将套接字 ID 存储在服务器上,每次刷新都会丢失) 问题是:有没有办法让 socket.io 中的连接保持活动状态,或者有办法链接 apache PHP 会话和 nodeJS 服务器?或者也许是一种使用 cookie 保持身份验证的方法(知道我必须存储敏感数据,如用户密码和密钥)?

【问题讨论】:

在 PHP 中使用 memcached 作为会话存储处理程序,然后使用 memcached module in nodejs - 现在您只需要会话标识符。 这似乎可行,但是 PHP 如何在 memcached 中存储会话?键是会话 ID,值是 $_SESSION 变量的 JSON 编码字符串或...? 您可以维护一个包含已登录用户的单独数组。使用套接字 ID 不是解决方案,因为在刷新时,套接字会断开连接并连接。如果用户注销,您将触发一个从该数组中删除用户数据的事件。但请注意用户在未注销的情况下关闭浏览器的情况。您的数组可能会变得太大,包含冗余数据 【参考方案1】:

您可以使用memcached as your session storage handler in PHP。 Memcached 是一个简单的键值存储,可以通过 TCP 访问;有一个memcached module available for Node.js。

PHP 使用会话 ID 作为键将会话存储在 memcached 中。存储在 memcached 中的会话数据(值)是一个序列化的 PHP 对象,略有不同。你可以在the SO question "Parse PHP Session in javascript" 阅读更多关于这个不寻常的序列化的信息。不过幸运的是,已经有一个 NPM 模块:php-unserialize


现在是操作方法。

假设

memcached 可通过 127.0.0.1:11211 访问 php.ini(或 php.d/memcache.ini)配置为:session.save_handler='memcached'session.save_path='tcp://127.0.0.1:11211' 你已经安装了所需的 NPM 模块(2):npm install memcached php-unserialize CLI 没问题

准备

首先,为了获得一些测试数据,保存以下 php 脚本 (s.php):

<?php
session_start();
$_SESSION['some'] = 'thing';
echo session_id()."\n";
print_r($_SESSION);

php s.php 执行它,它应该把东西放在标准输出中:

74ibpvem1no6s-s-ros60om3mlo5
Array
(
    [some] => thing
)

好的,现在我们知道会话 id (74ibpvem1no6s-s-ros60om3mlo5),并确认会话数据已设置。要确认它在 memcached 中,您可以运行memcached-tool 127.0.0.1:11211 dump,它提供已知键值对的转储,例如我的测试床中有两个:

Dumping memcache contents
  Number of buckets: 1
  Number of items  : 3
Dumping bucket 2 - 3 total items
add 74ibpvem1no6s-s-ros60om3mlo5 0 1403169638 17
some|s:5:"thing";
add 01kims55ut0ukcko87ufh9dpv5 0 1403168854 17
some|s:5:"thing";

到目前为止,我们已经 1) 在 php 中创建了一个会话 id,2) 将来自 php 的会话数据存储在 memcached 中,以及 3) 通过 CLI 确认数据存在。

使用 Node.js 进行检索

这部分其实很简单。 NPM 模块已经完成了大部分繁重的工作。我编写了一个通过 CLI 运行的小 Node.js 脚本,但你明白了:

var Memcached = require('memcached');
var PHPUnserialize = require('php-unserialize');

var mem = new Memcached('127.0.0.1:11211'); // connect to local memcached
var key = process.argv[2]; // get from CLI arg

console.log('fetching data with key:',key);
mem.get(key,function(err,data)  // fetch by key
        if ( err ) return console.error(err); // if there was an error
        if ( data === false ) return console.error('could not retrieve data'); // data is boolean false when the key does not exist
        console.log('raw data:',data); // show raw data
        var o = PHPUnserialize.unserializeSession(data); // decode session data
        console.log('parsed obj:',o); // show unserialized object
);

假设上面保存为m.js,它可以用node m.js 74ibpvem1no6s-s-ros60om3mlo5运行,它会输出类似:

fetching data with key: 74ibpvem1no6s-s-ros60om3mlo5
raw data: some|s:5:"thing";
parsed obj:  some: 'thing' 

警告/问题

我的一个 PHP 应用程序在会话值中存储了一些二进制数据(即加密),但密钥和正常会话对象保持不变(如上例所示)。在这种情况下,memcached-tool &lt;host:port&gt; dump 将格式错误的序列化会话字符串 打印到标准输出;我认为这可能与标准输出隔离,但我错了。 使用PHPUnserialize.unserializeSession时,解析数据也有问题(以|分隔)。我在网上尝试了其他一些会话反序列化方法,但没有任何成功。我会假设 memcached 在内部维护正确的数据,因为它与本机 PHP 会话保存处理程序一起使用,所以,在撰写本文时,我不太确定它是反序列化方法还是 memcached NPM 模块只是'没有正确检索/解释数据。当坚持使用 ascii 或 utf-8 等非二进制数据时,它应该可以按预期工作。

【讨论】:

好的,这就是我所在的位置:在设置 memcached 之后,我通过 PHPSESSID cookie(在 socket.io 授权中)获得了 PHP 会话 ID。但是我无法保存这个php会话ID(将它保存在handshake.session_id中不起作用,我无法使用socket.handshake.socket_id在socket.on('connection')中取回它。为什么??有吗?另一种保存这个 PHP 会话 ID 并在 socket.on('connection') 回调中取回它的方法?thx @MagixKiller,为什么要在 Node.js 端保存会话 ID?每次建立新的 socket.io 连接时,如果用户已通过身份验证,请使用 PHPSESSID 在 memcached 中查找(例如mem.get(PHPSESSID,function(e,v)authorized = v.isLoggedIn);。登录/注销将在 PHP 端进行管理并由 Node.js 信任。您甚至可以尽可能在会话对象中存储特权信息,例如$_SESSION['username']='MagixKiller'; $_SESSION['isLoggedIn']=true; 是的,但是如何获取 PHP 会话 ID 呢?在 on('connection') 事件期间,我们无法在套接字期间访问 cookie,可以吗?那么,如何在 mem.get(PHPSESSID, function() ) 中获取 PHPSESSID; ?还是在哪里定义的? 是的,第一个解决方案有效:我可以使用 var PHPSESSID = cookie.parse(socket.request.headers.cookie).PHPSESSID 在 on('connection') 中获取 cookie; !!!非常感谢您的帮助 ;-) 我在 CentOS 上使用 PHP-FPM,它存储密钥的方式略有不同:memc.sess.key.49d64hkl1pb7q2emftke823k76 所以在 Node.js 代码中,我的密钥变为 var key = "memc.sess.key." + process.argv[2]【参考方案2】:

我认为这个链接会对你有所帮助 https://simplapi.wordpress.com/2012/04/11/php-nodejs-session-share-memcache/

【讨论】:

【参考方案3】:

虽然线程很旧,但我想推荐我用于我的项目的线程。 除了 memcached,您还可以使用 Redis 进行会话处理。 我已经使用phpredis 作为 php redis 客户端。而不是将会话存储到可以保存在 Redis 中的文件。大部分繁重的工作将由 apache 完成。对于每个请求,apache 都会将会话值附加到 cookie 中。它会从每个请求中读取会话值并对其进行验证。

将php会话保存到redis所需的设置也很简单。

session.save_handler = redis session.save_path = "tcp://host1:6379?weight=1, tcp://host2:6379?weight=2&timeout=2.5, tcp://host3:6379?weight=2"

就是这样。这将使php将会话保存到redis而不是文件。这也会将存储在文件中的会话移动到 redis。

【讨论】:

【参考方案4】:

如果您的项目将会话存储在数据库中(有些会这样做),那么您可以考虑使用数据库作为传输媒介。

如果在您的特定情况下的分析显示有希望,那么可以使用 node-mysql(或类似的) - 请参阅:link

【讨论】:

【参考方案5】:

zamnuts 的回答帮助我完成了身份验证,并且是我已经采用的方法。感谢那。

我发帖的原因是我在使用时的一些原因:

var PHPUnserialize = require('php-unserialize');

一直给我错误

SyntaxError: String length mismatch

我不知道为什么?我写了一个函数来为我完成这项工作,并想分享它以防它可以帮助其他人。

function Unserialize(data)

  var result = ;

  if(data !== undefined)

    var preg = data.replace(/(^|s:[0-9]+:)|(^|i:)|(^|b:)|(")|(;$)/g,'').split(';'); 

    var a = [];
    preg.forEach(function(value)
      a.push(value.split('|'));
    );

    var b = [];
    a.forEach(function(value)
      if(Array.isArray(value))
        Array.prototype.push.apply(b, value);
      else
        b.push(value);  
      
    );

    var arr_A = [];
    var arr_B = [];
    b.forEach(function(value, k)
      if(k % 2 == 0)
        arr_A.push(value);
      else
        arr_B.push(value);
           
     );


     if (arr_A == null) return ;

     for (var i = 0, l = arr_A.length; i < l; i++) 
       if (arr_B) 
        result[arr_A[i]] = arr_B[i];
        else 
        result[arr_A[i][0]] = arr_A[i][1];
       
     

  

  return result;


我只是这样称呼它:

var PHPUnserialize = Unserialize;

memcached.get(key, function(err, data)
   var memData = PHPUnserialize(data);
   console.log(memData.is_logged_in);
); 

您应该能够相当轻松地修改正则表达式以满足您的需要。

【讨论】:

以上是关于PHP、nodeJS 和会话的主要内容,如果未能解决你的问题,请参考以下文章

如何将用户名从php会话存储到nodejs中的socket.io库和页面刷新时套接字连接断开。为啥?

PHP + socket.io(会话、授权和安全问题)

nodejs / express中的数据库会话支持[关闭]

验证 socket.io/nodejs 的用户

nodejs:会话存储在哪里?啥是连接蒙戈?

使用 JSONWebToken 在 NodeJS 和 Angular 之间进行会话管理