php 为 socket.io/nodejs 验证用户

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6502031/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-26 00:35:05  来源:igfitidea点击:

Authenticate user for socket.io/nodejs

phpsessionnode.jssocket.io

提问by John

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.

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

回答by Alfred

Update

更新

Requirements:

要求:

  1. First have redis running.
  2. Next fire up socket.io.
  3. Finally upload/host PHP(has dependencies in archive).
  1. 首先运行redis。
  2. 接下来启动socket.io。
  3. 最后上传/托管 PHP(在存档中有依赖项)。

Socket.io

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

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

带有 openid 身份验证的 php => http://dl.dropbox.com/u/314941/6503745/php.tar.gz

After login you have to reload client.phpto authenticate

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



p.s: 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 Googlefor example), Facebook Connect(just name a few options).

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

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.

我的问题是,一旦他们通过 php/session 进行身份验证,那么验证用户身份以查看他们是否具有使用 socket.io 访问 nodejs 服务器的正确登录权限的过程是什么?我不希望此人访问 nodejs/socket.io 函数/服务器,除非他们通过 php 登录进行了身份验证。

Add the unique session_idto 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 redisbecause that is going to be lightning fast/AWESOME :). Right now I am faking the PHP communication from redis-cli

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

Install Redis

安装Redis

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

下载 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

Start Redis-server

启动Redis服务器

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

Socket.io

Socket.io

npm dependencies

npm 依赖项

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

如果npm尚未安装,则首先访问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

列出我已安装的依赖项,如果不兼容,您也应该安装这些依赖项 npm ls

alfred@alfred-laptop:~/node/socketio-demo$ npm ls
/home/alfred/node/socketio-demo
├─┬ [email protected] 
│ ├── [email protected] 
│ ├── [email protected] 
│ └── [email protected] 
├── [email protected] 
├── [email protected] 
└─┬ [email protected] 
  ├── [email protected] 
  └── [email protected] 

Code

代码

server.js

服务器.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

要运行服务器只需运行 node server.js

client.php

客户端.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 deniedis shown, but when you add the session_idalso shown in browser to redis server the message access grantedwill 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 ramom807vt1io3sqvmc8m4via1into redis after which access has been granted.

当您从网络浏览器加载网页(PHP 文件)时,access denied会显示该消息,但是当您将session_id浏览器中也显示的内容添加到 redis 服务器时,access granted将显示该消息。当然,通常您不会进行任何复制粘贴,而是让 PHP 直接与 Redis 通信。授权. 但是对于这个演示,您将ramom807vt1io3sqvmc8m4via1在授予访问权限后将 SID放入 redis。

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

回答by rcode

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_pathdirective in your php.ini.

请记住,会话只是存储在 php 会话目录中的文件。node.js 从 cookie 中获取 session id 然后检查 session 是否真的存在于 session 目录中不会有问题。要获取会话目录的路径,请参阅php.ini 中的session.save_path指令。

回答by marksyzm

Here's the unserialize and utf8 code if you want it too, originally derived from phpjs.org- had to edit it a bit to make it work with node.js so fish around and compare if you want

如果您也需要,这里是反序列化和 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:[email protected])
    // +     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;

回答by user1274820

I was looking over the solutions here and decided to give what rcode said a try because it seemed so much easier than the gigantic wall of code accepted answer.

我正在查看这里的解决方案,并决定尝试一下 rcode 所说的内容,因为它似乎比巨大的代码墙要容易得多。

It ended up working nicely and is quite easy to do.

它最终工作得很好,而且很容易做到。

I did end up installing a few dependencies which I wanted to avoid but is relatively easy to do with node.

我确实最终安装了一些我想避免的依赖项,但使用 node.js 相对容易。

Type the following in console:

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

npm install cookie

npm install cookie

npm install php-unserialize

npm install php-unserialize

This solution uses the session files on the machine - you shouldn't have to change this line.

此解决方案使用机器上的会话文件 - 您不必更改此行。

session.save_handler = files

session.save_handler = files

^ Should be like this in your php.ini file (default).

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

(People suggested using memcache, but it seemed like a headache to switch over to that system.)

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

Here is the super simple code to retrieve the session data:

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

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:

结果:

Results

结果

Edit: I just wanted to add that it may be easier to just have PHP tell your Node.js server when someone logs in and pass the credentials along there.

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

I explain how to do this pretty easily in another answer.

我在另一个答案中解释了如何很容易地做到这一点。

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

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