I have an classical apache server delivering php files, and a nodeJS server (with socket.io, but whithout express/connect) used for real-time event management on that PHP website. I sometimes need to authenticate the clients connecting to the nodeJS server, but this authentication is lost when the user reloads the page, because it also reloads the socket.io client (I store the socket ID on the server, which gets lost at each refresh)
The question is: Is there a way to keep the connection alive in socket.io, or a way to link the apache PHP sessions and the nodeJS server? Or maybe a way to keep this authentication using cookies (knowing I must store sensitive data like user passwords and keys)?
You can use memcached
as your session storage handler in PHP. Memcached is a simple key value store that can be accessed via TCP; there is a memcached module available for Node.js.
PHP stores the session in memcached by using the session id as the key. The session data (value) stored in memcached is a serialized PHP object, with a slight twist. You can read more about this unusual serialization at the SO question "Parse PHP Session in Javascript". Luckily though, there is already an NPM module out there: php-unserialize
.
Now for the How-To.
Assumptions
session.save_handler='memcached'
and session.save_path='tcp://127.0.0.1:11211'
npm install memcached php-unserialize
Prepare
First, just to get some test data to work with, save the following php script (s.php
):
<?php
session_start();
$_SESSION['some'] = 'thing';
echo session_id()."\n";
print_r($_SESSION);
Execute it with php s.php
, and it should put stuff in stdout:
74ibpvem1no6ssros60om3mlo5
Array
(
[some] => thing
)
Ok, now we know the session id (74ibpvem1no6ssros60om3mlo5
), and have confirmed that the session data was set. To confirm it is in memcached, you can run memcached-tool 127.0.0.1:11211 dump
which provides a dump of known key:value pairs, for example I have two in my test bed:
Dumping memcache contents
Number of buckets: 1
Number of items : 3
Dumping bucket 2 - 3 total items
add 74ibpvem1no6ssros60om3mlo5 0 1403169638 17
some|s:5:"thing";
add 01kims55ut0ukcko87ufh9dpv5 0 1403168854 17
some|s:5:"thing";
So far we have 1) created a session id in php, 2) stored session data from php in memcached, and 3) confirmed the data exists via CLI.
Retrieval with Node.js
This part is actually really easy. Most of the heavy-lifting has already been done by the NPM modules. I cooked up a little Node.js script that runs via CLI, but you get the picture:
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
});
Assuming the above is saved as m.js
, it can be run with node m.js 74ibpvem1no6ssros60om3mlo5
which will output something like:
fetching data with key: 74ibpvem1no6ssros60om3mlo5
raw data: some|s:5:"thing";
parsed obj: { some: 'thing' }
Warnings/Gotchas
One of my PHP applications stores some binary data in the session values (i.e. encrypted), but the keys and the normal session object remain intact (as in the example above). In this case, memcached-tool <host:port> dump
printed a malformed serialized session string to stdout; I thought this might be isolated to stdout, but I was wrong. When using PHPUnserialize.unserializeSession
, it also had trouble parsing the data (delimited by |
). I tried a few other session deserialization methods out on the net, but did not have any success. I would assume memcached is maintaining the correct data internally since it works with the native PHP session save handler, so, at the time of this writing, I'm not quite sure if it is the deserialization methods or if the memcached NPM module simply isn't retrieving/interpreting the data correctly. When sticking with non-binary data like ascii or utf-8, it should work as intended.