为socket.io/nodejs验证用户

时间:2011-06-28 05:38:12

标签: php session node.js socket.io

我有一个php登录,用户输入用户名/密码,它会根据登录信息检查mysql数据库。如果经过身份验证,则会通过php创建会话,用户现在可以使用php会话访问系统。我的问题是,一旦他们通过php / session进行身份验证,授权用户查看他们是否具有使用socket.io访问nodejs服务器的正确登录权限的过程是什么?我不希望这个人有权访问nodejs / socket.io函数/服务器,除非他们通过php登录进行了身份验证。

4 个答案:

答案 0 :(得分:62)

更新

要求:

  1. 首先让redis运行。
  2. 接下来启动socket.io。
  3. 最后上传/托管PHP(在归档中有依赖关系)。
  4. 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

    登录后,您必须重新加载client.php以进行身份​​验证


    p.s:我真的不喜欢创建另一个可能不安全的密码的概念。我建议您查看openID(例如,通过Google),Facebook Connect(仅举几个选项)。

      

    我的问题是,一旦他们进行身份验证   通过php / session会是什么   验证用户的过程   看看他们是否有正确的登录信息   访问nodejs服务器的权限   用socket.io?我不想要这个人   有权访问nodejs / socket.io   功能/服务器,除非他们有   通过php登录验证。

    将唯一session_id添加到允许ID的列表/集合中,以便socket.io可以authorize(搜索授权功能)该连接。我会让PHP使用redis与node.js进行通信,因为这将是闪电般的快速/真棒:)。现在我正在伪造来自redis-cli

    的PHP通信

    安装Redis

    Download redis =>现在可以从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
    

    启动Redis-server

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

    Socket.io

    npm依赖项

    如果尚未安装npm,请先访问http://npmjs.org

    npm install express
    npm install socket.io
    npm install redis
    

    列出我已经安装的依赖项,如果根据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');
    });
    

    要运行服务器,只需运行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>
    

    测试身份验证

    当您从网络浏览器加载网页(PHP文件)时,会显示消息access denied,但当您将浏览器中显示的session_id添加到redis服务器时,会显示消息{{1将显示。当然,通常你不会做任何复制粘贴,只是让PHP直接与Redis通信。auth。但是对于此演示,您将把SID access granted放入redis,然后授予访问权限。

    ramom807vt1io3sqvmc8m4via1

答案 1 :(得分:8)

请记住,会话只是存储在php会话目录中的文件。 node.js从cookie中获取会话ID并检查会话目录中是否确实存在会话ID也不成问题。要获取sessions目录的路径,请参阅php.ini中的 session.save_path 指令。

答案 2 :(得分:0)

这里是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;

答案 3 :(得分:0)

我在这里查看解决方案,并决定提供rcode所说的尝试,因为它似乎比巨大的代码墙接受答案容易得多。

它最终很好地工作并且很容易做到。

我最终安装了一些我想避免的依赖项,但是对于节点来说相对容易。

在控制台中输入以下内容:

npm install cookie

npm install php-unserialize

此解决方案使用计算机上的会话文件 - 您不应该更改此行。

session.save_handler = files

^你的php.ini文件中应该是这样的(默认)。

(人们建议使用memcache,但切换到该系统似乎很头疼。)

以下是检索会话数据的超级简单代码:

var cookie = require('cookie');
var fs = require('fs');
var phpUnserialize = require('php-unserialize');

//This should point to your php session directory.
//My php.ini says session.save_path = "${US_ROOTF}/tmp"
var SESS_PATH = "C:/SomeDirectory/WhereYourPHPIs/tmp/";

io.on('connection', function(socket) {
    //I just check if cookies are a string - may be better method
    if(typeof socket.handshake.headers.cookie === "string") {
        var sid = cookie.parse(socket.handshake.headers.cookie);
        if(typeof sid.PHPSESSID === "undefined") {
          console.log("Undefined PHPSESSID");
        }
        else {
            console.log("PHP Session ID: " + sid.PHPSESSID);
            fs.readFile(SESS_PATH + "sess_" + sid.PHPSESSID, 'utf-8', function(err,data) {
                if(!err) {
                    console.log("Session Data:");
                    var sd = phpUnserialize.unserializeSession(data);
                    console.log(sd);
                }
                else {
                   console.log(err);
                }
            });
        }
    }
}

<强>结果:

Results

编辑:我只是想补充一点,当有人登录并传递凭据时,让PHP告诉您的Node.js服务器可能更容易。

我在另一个答案中解释了如何轻松地做到这一点。

https://stackoverflow.com/a/49864533/1274820