Authenticate user for socket.io/nodejs

I have a php login, the user puts in a username/password, it checks the mysql db against the login information. If authenticated a session is created via php and the user can now access the system with the php session. My question is once they authenticate via php/session what would be the process to authorize the user to see if they have the right login permissions to access a nodejs server with socket.io? I dont want the person to have access to the nodejs/socket.io function/server unless they have authenticated via the php login.


Update

Requirements:

  • First have redis running.
  • Next fire up socket.io.
  • Finally upload/host PHP(has dependencies in archive).
  • Socket.io

    var express = require('express'),
            app         = express.createServer(),
            sio         = require('socket.io'),
            redis   = require("redis"),
        client  = redis.createClient(),
            io          = null;
    
    /**
     *  Used to parse cookie
     */
    function parse_cookies(_cookies) {
        var cookies = {};
    
        _cookies && _cookies.split(';').forEach(function( cookie ) {
            var parts = cookie.split('=');
            cookies[ parts[ 0 ].trim() ] = ( parts[ 1 ] || '' ).trim();
        });
    
        return cookies;
    }
    
    app.listen(3000, "localhost");
    io = sio.listen(app);
    
    io.of('/private').authorization(function (handshakeData, callback) {
            var cookies = parse_cookies(handshakeData.headers.cookie);
    
            client.get(cookies.PHPSESSID, function (err, reply) {
                    handshakeData.identity = reply;
                    callback(false, reply !== null);
            });
    }).on('connection' , function (socket) {
            socket.emit('identity', socket.handshake.identity);
    });
    

    PHP

    php with openid authentication => http://dl.dropbox.com/u/314941/6503745/php.tar.gz

    After login you have to reload client.php to authenticate


    ps: I really don't like the concept of creating even another password which is probably is going to be unsafe. I would advice you to have a look at openID(via Google for example), Facebook Connect(just name a few options).

    My question is once they authenticate via php/session what would be the process to authenticate the user to see if they have the right login permissions to access a nodejs server with socket.io? I dont want the person to have access to the nodejs/socket.io function/server unless they have authenticated via the php login.

    Add the unique session_id to a list/set of allowed ids so that socket.io can authorize(search for authorization function) that connection. I would let PHP communicate with node.js using redis because that is going to be lightning fast/AWESOME :). Right now I am faking the PHP communication from redis-cli

    Install Redis

    Download redis => Right now the stable version can be downloaded from: http://redis.googlecode.com/files/redis-2.2.11.tar.gz

    alfred@alfred-laptop:~$ mkdir ~/6502031
    alfred@alfred-laptop:~/6502031$ cd ~/6502031/
    alfred@alfred-laptop:~/6502031$ tar xfz redis-2.2.11.tar.gz 
    alfred@alfred-laptop:~/6502031$ cd redis-2.2.11/src
    alfred@alfred-laptop:~/6502031/redis-2.2.11/src$ make # wait couple of seconds
    

    Start Redis-server

    alfred@alfred-laptop:~/6502031/redis-2.2.11/src$ ./redis-server 
    

    Socket.io

    npm dependencies

    If npm is not already installed , then first visit http://npmjs.org

    npm install express
    npm install socket.io
    npm install redis
    

    listing the dependencies I have installed and which you should also probably install in case of incompatibility according to npm ls

    alfred@alfred-laptop:~/node/socketio-demo$ npm ls
    /home/alfred/node/socketio-demo
    ├─┬ express@2.3.12 
    │ ├── connect@1.5.1 
    │ ├── mime@1.2.2 
    │ └── qs@0.1.0 
    ├── hiredis@0.1.12 
    ├── redis@0.6.0 
    └─┬ socket.io@0.7.2 
      ├── policyfile@0.0.3 
      └── socket.io-client@0.7.2 
    

    Code

    server.js

    var express = require('express'),
            app         = express.createServer(),
            sio         = require('socket.io'),
            redis   = require("redis"),
        client  = redis.createClient(),
            io          = null;
    
    /**
     *  Used to parse cookie
     */
    function parse_cookies(_cookies) {
        var cookies = {};
    
        _cookies && _cookies.split(';').forEach(function( cookie ) {
            var parts = cookie.split('=');
            cookies[ parts[ 0 ].trim() ] = ( parts[ 1 ] || '' ).trim();
        });
    
        return cookies;
    }
    
    app.listen(3000, "localhost");
    io = sio.listen(app);
    
    io.configure(function () {
      function auth (data, fn) {
        var cookies = parse_cookies(data.headers.cookie);
        console.log('PHPSESSID: ' + cookies.PHPSESSID);
    
            client.sismember('sid', cookies.PHPSESSID, function (err , reply) {
                fn(null, reply);    
            });
      };
    
      io.set('authorization', auth);
    });
    
    io.sockets.on('connection', function (socket) {
      socket.emit('access', 'granted');
    });
    

    To run server just run node server.js

    client.php

    <?php
    
    session_start();
    
    echo "<h1>SID: " . session_id() . "</h1>";
    ?>
    <html>
    <head>
        <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script>
        <script src="http://localhost:3000/socket.io/socket.io.js"></script>
    </head>
    <body>
        <p id="text">access denied</p>
        <script>
            var socket = io.connect('http://localhost:3000/');
            socket.on('access', function (data) {
                $("#text").html(data);
            });
        </script>
    </body>
    

    Test authentication

    When you load the webpage(PHP-file) from your web-browser the message access denied is shown, but when you add the session_id also shown in browser to redis server the message access granted will be shown. Of course normally you would not be doing any copy pasting but just let PHP communicate with Redis directly. AUTH . But for this demo you will put SID ramom807vt1io3sqvmc8m4via1 into redis after which access has been granted.

    alfred@alfred-laptop:~/database/redis-2.2.0-rc4/src$ ./redis-cli 
    redis> sadd sid ramom807vt1io3sqvmc8m4via1
    (integer) 1
    redis> 
    

    Remember that sessions are just files stored in the php sessions directory. It won't be a problem for node.js to get the session id from the cookie and then check if the session really exists in the sessions directory. To get the path of the sessions directory refer to the session.save_path directive in your php.ini.


    这里是unserialize和utf8的代码,如果你也想要的话,最初是从phpjs.org派生出来的 - 不得不对它进行编辑,使它与node.js一起工作,这样你就可以随时查找并比较你是否想要

    function utf8_decode (str_data) {
        // http://kevin.vanzonneveld.net
        // +   original by: Webtoolkit.info (http://www.webtoolkit.info/)
        // +      input by: Aman Gupta
        // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
        // +   improved by: Norman "zEh" Fuchs
        // +   bugfixed by: hitwork
        // +   bugfixed by: Onno Marsman
        // +      input by: Brett Zamir (http://brett-zamir.me)
        // +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
        // *     example 1: utf8_decode('Kevin van Zonneveld');
        // *     returns 1: 'Kevin van Zonneveld'
        var tmp_arr = [],
            i = 0,
            ac = 0,
            c1 = 0,
            c2 = 0,
            c3 = 0;
    
        str_data += '';
    
        while (i < str_data.length) {
            c1 = str_data.charCodeAt(i);
            if (c1 < 128) {
                tmp_arr[ac++] = String.fromCharCode(c1);
                i++;
            } else if (c1 > 191 && c1 < 224) {
                c2 = str_data.charCodeAt(i + 1);
                tmp_arr[ac++] = String.fromCharCode(((c1 & 31) << 6) | (c2 & 63));
                i += 2;
            } else {
                c2 = str_data.charCodeAt(i + 1);
                c3 = str_data.charCodeAt(i + 2);
                tmp_arr[ac++] = String.fromCharCode(((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
                i += 3;
            }
        }
    
        return tmp_arr.join('');
    }
    exports.utf8_decode = utf8_decode;
    
    function unserialize (data) {
        // http://kevin.vanzonneveld.net
        // +     original by: Arpad Ray (mailto:arpad@php.net)
        // +     improved by: Pedro Tainha (http://www.pedrotainha.com)
        // +     bugfixed by: dptr1988
        // +      revised by: d3x
        // +     improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
        // +        input by: Brett Zamir (http://brett-zamir.me)
        // +     improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
        // +     improved by: Chris
        // +     improved by: James
        // +        input by: Martin (http://www.erlenwiese.de/)
        // +     bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
        // +     improved by: Le Torbi
        // +     input by: kilops
        // +     bugfixed by: Brett Zamir (http://brett-zamir.me)
        // -      depends on: utf8_decode
        // %            note: We feel the main purpose of this function should be to ease the transport of data between php & js
        // %            note: Aiming for PHP-compatibility, we have to translate objects to arrays
        // *       example 1: unserialize('a:3:{i:0;s:5:"Kevin";i:1;s:3:"van";i:2;s:9:"Zonneveld";}');
        // *       returns 1: ['Kevin', 'van', 'Zonneveld']
        // *       example 2: unserialize('a:3:{s:9:"firstName";s:5:"Kevin";s:7:"midName";s:3:"van";s:7:"surName";s:9:"Zonneveld";}');
        // *       returns 2: {firstName: 'Kevin', midName: 'van', surName: 'Zonneveld'}
        var that = this;
        var utf8Overhead = function (chr) {
            // http://phpjs.org/functions/unserialize:571#comment_95906
            var code = chr.charCodeAt(0);
            if (code < 0x0080) {
                return 0;
            }
            if (code < 0x0800) {
                return 1;
            }
            return 2;
        };
    
    
        var error = function (type, msg, filename, line) {
            console.log('[[[[[[[[[[[[[[[[[[ERROR]]]]]]]]]]]]]]]]]]]','msg:', msg, 'filename:',filename, 'line:',line);
        };
        var read_until = function (data, offset, stopchr) {
                if (stopchr == ';' && !data.match(/;$/)) data += ';';
            var buf = [];
            var chr = data.slice(offset, offset + 1);
            var i = 2;
            while (chr != stopchr) {
                if ((i + offset) > data.length) {
                    error('Error', 'Invalid','php.js','126');
                }
                buf.push(chr);
                chr = data.slice(offset + (i - 1), offset + i);
                i += 1;
                //console.log('i:',i,'offset:',offset, 'data:',data,'chr:',chr,'stopchr:',stopchr);
            }
            return [buf.length, buf.join('')];
        };
        var read_chrs = function (data, offset, length) {
            var buf;
    
            buf = [];
            for (var i = 0; i < length; i++) {
                var chr = data.slice(offset + (i - 1), offset + i);
                buf.push(chr);
                length -= utf8Overhead(chr);
            }
            return [buf.length, buf.join('')];
        };
        var _unserialize = function (data, offset) {
            var readdata;
            var readData;
            var chrs = 0;
            var ccount;
            var stringlength;
            var keyandchrs;
            var keys;
    
            if (!offset) {
                offset = 0;
            }
            var dtype = (data.slice(offset, offset + 1)).toLowerCase();
    
            var dataoffset = offset + 2;
            var typeconvert = function (x) {
                return x;
            };
    
            switch (dtype) {
            case 'i':
                typeconvert = function (x) {
                    return parseInt(x, 10);
                };
                readData = read_until(data, dataoffset, ';');
                chrs = readData[0];
                readdata = readData[1];
                dataoffset += chrs + 1;
                break;
            case 'b':
                typeconvert = function (x) {
                    return parseInt(x, 10) !== 0;
                };
                readData = read_until(data, dataoffset, ';');
                chrs = readData[0];
                readdata = readData[1];
                dataoffset += chrs + 1;
                break;
            case 'd':
                typeconvert = function (x) {
                    return parseFloat(x);
                };
                readData = read_until(data, dataoffset, ';');
                chrs = readData[0];
                readdata = readData[1];
                dataoffset += chrs + 1;
                break;
            case 'n':
                readdata = null;
                break;
            case 's':
                ccount = read_until(data, dataoffset, ':');
                chrs = ccount[0];
                stringlength = ccount[1];
                dataoffset += chrs + 2;
    
                readData = read_chrs(data, dataoffset + 1, parseInt(stringlength, 10));
                chrs = readData[0];
                readdata = readData[1];
                dataoffset += chrs + 2;
                if (chrs != parseInt(stringlength, 10) && chrs != readdata.length) {
                    error('SyntaxError', 'String length mismatch','php.js','206');
                }
    
                // Length was calculated on an utf-8 encoded string
                // so wait with decoding
                readdata = utf8_decode(readdata);
                break;
            case 'a':
                readdata = {};
    
                keyandchrs = read_until(data, dataoffset, ':');
                chrs = keyandchrs[0];
                keys = keyandchrs[1];
                dataoffset += chrs + 2;
    
                for (var i = 0; i < parseInt(keys, 10); i++) {
                    var kprops = _unserialize(data, dataoffset);
                    var kchrs = kprops[1];
                    var key = kprops[2];
                    dataoffset += kchrs;
    
                    var vprops = _unserialize(data, dataoffset);
                    var vchrs = vprops[1];
                    var value = vprops[2];
                    dataoffset += vchrs;
    
                    readdata[key] = value;
                }
    
                dataoffset += 1;
                break;
            default:
                error('SyntaxError', 'Unknown / Unhandled data type(s): ' + dtype,'php.js','238');
                break;
            }
            return [dtype, dataoffset - offset, typeconvert(readdata)];
        };
    
        return _unserialize((data + ''), 0)[2];
    }
    exports.unserialize = unserialize;
    
    链接地址: http://www.djcxy.com/p/38010.html

    上一篇: Scala返回类型为元组

    下一篇: 为socket.io/nodejs验证用户