web-dev-qa-db-ja.com

socket.io/nodejsのユーザーを認証する

私はPHPログインを持っており、ユーザーはユーザー名/パスワードを入力し、ログイン情報に対してmysql dbをチェックします。認証された場合、セッションはphpを介して作成され、ユーザーはphpセッションでシステムにアクセスできるようになります。私の質問は、php。 phpログイン経由で認証されていない限り、nodejs/socket.io関数/サーバーにアクセスできるようにしたくありません。

40
John

更新

要件:

  1. 最初にredisを実行します。
  2. 次に、socket.ioを起動します。
  3. 最後に、PHPをアップロード/ホストします(アーカイブに依存関係があります)。

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

openid認証付きのphp => http://dl.dropbox.com/u/314941/6503745/php.tar.gz

ログイン後、client.phpをリロードして認証する必要があります


p.s:私は、おそらく安全ではない別のパスワードを作成するという概念が本当に好きではありません。 openID (via Google など)、 Facebook Connect (ほんのいくつかのオプションに名前を付けてください)をご覧になることをお勧めします。

私の質問は、php/sessionを介して認証されると、socket.ioでnodejsサーバーにアクセスするための正しいログイン権限があるかどうかを確認するためにユーザーを認証するプロセスは何ですか? phpログイン経由で認証されていない限り、nodejs/socket.io関数/サーバーにアクセスできるようにしたくありません。

一意の session_id を許可されたIDのリスト/セットに追加して、socket.ioが authorize (認証機能を検索)できるようにします。 PHP redis を使用してnode.jsと通信します。今私はPHPからの通信redis-cli

Redisをインストールする

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
├─┬ [email protected] 
│ ├── [email protected] 
│ ├── [email protected] 
│ └── [email protected] 
├── [email protected] 
├── [email protected] 
└─┬ [email protected] 
  ├── [email protected] 
  └── [email protected] 

コード

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>

認証をテストする

WebブラウザーからWebページ(PHPファイル)を読み込むと、メッセージaccess deniedが表示されますが、ブラウザーに表示されるsession_idを追加すると、サーバーaccess grantedが再表示されます表示されます。もちろん、通常はコピーの貼り付けは行わず、単にPHP Redisと直接通信します。auth。ただし、このデモでは、SID ramom807vt1io3sqvmc8m4via1をredisに配置してからアクセスを許可します。

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

セッションは、phpセッションディレクトリに保存されているファイルであることを忘れないでください。 node.jsがCookieからセッションIDを取得し、セッションが実際にセッションディレクトリに存在するかどうかを確認することは問題になりません。 sessionsディレクトリのパスを取得するには、php.iniのsession.save_pathディレクティブを参照してください。

8
rcode

元々 phpjs.org から派生した、必要に応じてunserializeとutf8のコードを次に示します

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;
0
marksyzm

私はここで解決策を見ていましたが、rcodeが答えを受け入れた巨大な壁よりもはるかに簡単に思えたので、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/127482

0
user1274820