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
Authenticate user for socket.io/nodejs
提问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:
要求:
- First have redis running.
- Next fire up socket.io.
- Finally upload/host PHP(has dependencies in archive).
- 首先运行redis。
- 接下来启动socket.io。
- 最后上传/托管 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.php
to 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 npm
is 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 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.. But for this demo you will put SID
ramom807vt1io3sqvmc8m4via1
into 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:
结果:
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.
我在另一个答案中解释了如何很容易地做到这一点。