Redis、Node.js 和 Socket.io:跨服务器认证和 node.js 理解 [英] Redis, Node.js, and Socket.io : Cross server authentication and node.js understanding
问题描述
我有一个在 Rails 和 Backbone.js 上运行的单页应用程序.我正在通过 Redis 使用 Node.js 向客户端推送和同步数据.我正在尝试了解、保护和优化套接字通信.
I got a single-page application running on Rails and Backbone.js. I'm using Node.js through Redis to push and synchronize data to clients. I am trying to understand, secure and optimize sockets communications.
使用 console.log 编写时,我看到一些日志行 (console.log('Redis connection on ..')
) 出现重复(见下文).
When writing with console.log I see some log lines (console.log('Redis connection on ..')
) are getting duplicate (see bellow).
谁能解释一下为什么?通过我的代码实现,是否有一些我不理解的特定 Node.js 行为?
Does anyone can explain me why ? Is there some specific Node.js behaviours I'm not understanding, through my code implementation ?
这是我的 Node.js 代码:
Here's my Node.js code:
var io = require('socket.io').listen(3003);
var redis = require('redis');
var cookie = require("cookie");
var redis_subs = redis.createClient(6379, "localhost");
var redis_auth = redis.createClient(6379, "localhost");
var authenticated = false;
var authentication_status = function (status) {
if (status === true) {
console.log('Authentication status is true.');
}
else {
console.log('Authentication status is false.');
}
authenticated = status;
};
redis_subs.subscribe('application_channel');
io.configure(function () {
io.set('authorization', function (data, callback) {
console.log('Cross authentication...');
if (data.headers.cookie) {
data.cookie = cookie.parse(data.headers.cookie);
data.sessionID = data.cookie['_validation_token_key'];
redis_auth.hget(["sessionStore", data.sessionID], function (err, session) {
if (err || !session) {
console.log('Socket.io authorization say false');
return callback(authentication_status(false), false);
}
else {
data.session = JSON.parse(session);
console.log('Socket.io authorization say true');
return callback(authentication_status(true), true);
}
});
}
else {
console.log('Socket.io authorization say false');
return callback(authentication_status(false), false);
}
});
});
io.on('connection', function(socket){
if (socket.handshake.session) {
var user_socket_channel = socket.handshake.session['user_socket_channel']
redis_subs.on('message', function(redis_channel, rawdata) {
console.log('Redis connection on '+redis_channel);
console.log('Handshaked Session, authenticated user. All channels allowed.');
var data = JSON.parse(rawdata);
if (data.channel) { var socket_channel = data.channel; }
else { var socket_channel = user_socket_channel; }
var rails_data = data.data;
console.log('Socket.io emiting on ['+socket_channel+']')
socket.emit(socket_channel, rails_data);
});
}
else {
redis_subs.on('message', function(redis_channel, rawdata) {
console.log('Redis connection on '+redis_channel);
console.log('No handshaked Session, unauthenticated user. Public channels allowed.');
var data = JSON.parse(rawdata);
var rails_data = data.data;
console.log('Socket.io emiting on [public]')
socket.emit('public', rails_data);
});
}
});
这是我测试它的方式,以及日志重复的特定用例:
Here is the way I am testing it, and the specific use case where log is getting duplicate :
在登录时加载页面,使用网络浏览器1(例如:Firefox);输出:
Loading the page while being logged-in, with web browser1 (e.g: Firefox); output:
info - socket.io started
Cross authentication...
Socket.io authorization say true
Authentication status is true.
debug - authorized
info - handshake authorized bJxm_IcWl2mKZT4Ed-kV
debug - setting request GET /socket.io/1/websocket/bJxm_IcWl2mKZT4Ed-kV
debug - set heartbeat interval for client bJxm_IcWl2mKZT4Ed-kV
debug - client authorized for
debug - websocket writing 1::
加载使用 browser2 注销的同一页面(例如:Chrome)
Loading the same page being logged-out with browser2 (e.g: Chrome)
Cross authentication...
Socket.io authorization say false
Authentication status is false.
debug - authorized
info - handshake unauthorized
通过 page/browser1 (Firefox) -> Rails -> Redis 发送一些数据,输出:
Sending some data through page/browser1 (Firefox) -> Rails -> Redis, output:
Redis connection on application_channel
Handshaked Session, authenticated user. All channels allowed.
Socket.io emiting on [hLTYXuvP+13aQlIT9CZiYc1i9eg=]
debug - websocket writing 5:::{"name":"hLTYXuvP+13aQlIT9CZiYc1i9eg=","args":[null]}
在浏览器2(Chrome,已注销)上重新加载页面,仍然输出
Reloading the page on browser2(Chrome, logged-out), still output
Cross authentication...
Socket.io authorization say false
Authentication status is false.
debug - authorized
info - handshake unauthorized
在 browser1/Firefox 上重新加载页面,并通过页面和 Rails 向 Redis 推送更多数据,输出:
Reloading the page on browser1/Firefox and pushing some more data to Redis through the page and Rails, output:
Redis connection on application_channel
Handshaked Session, authenticated user. All channels allowed.
Socket.io emiting on [hLTYXuvP+13aQlIT9CZiYc1i9eg=]
Redis connection on application_channel
Handshaked Session, authenticated user. All channels allowed.
Socket.io emiting on [hLTYXuvP+13aQlIT9CZiYc1i9eg=]
debug - websocket writing 5:::{"name":"hLTYXuvP+13aQlIT9CZiYc1i9eg=","args":[null]}
如您所见,事件被复制,Redis 侦听器现在捕获两个连接;每次我重新加载 page1/Firefox 时,它都会再复制一次(3,4,5...).
As you can see, the event got duplicated, and the Redis listener now catch two connections; Every time I'll reload page1/Firefox, it will duplicate it one more time (3,4,5...).
我不明白这种行为.我错过了什么?顺便说一句,正如你所看到的,我不太了解 Socket.io configure
、set authorization
和 callback
的目标,返回 true 或 false,因为 Node 正在访问 io.on('connection')
和 Redis 侦听器.
I don't understand this behaviours. What am I missing?
By the way, as you can see, I don't really understand either the goal of Socket.io configure
, set authorization
and callback
, returning true or false, as Node is reaching to io.on('connection')
and Redis listeners anyway.
推荐答案
好的,我知道答案了.我很难弄清楚,已经找了几个小时......
Ok, I've got my answer. I had a hard time figuring it out, been looking for hours ...
这是使我能够使其工作的相关主题(回答和评论):
Here's the related topic that allowed me to make it work (answer AND comments):
这是我的代码:
io.on('connection', function(client){
console.log('Client id #'+client.id);
if (client.handshake.session) {
var user_socket_channel = client.handshake.session['user_socket_channel'];
}
var redis_listener = function(redis_channel, rawdata) {
console.log('Redis connection on '+redis_channel);
var data = JSON.parse(rawdata);
if (data.channel) { var socket_channel = data.channel; }
else {
if (user_socket_channel) { var socket_channel = user_socket_channel; }
else { var socket_channel = 'public' }
}
var rails_data = data.data;
console.log('Socket.io emiting on ['+socket_channel+']');
client.emit(socket_channel, rails_data);
};
redis_subs.on('message', redis_listener);
client.on('disconnect', function(){
console.log('Client disconnect, removing redis listener..');
redis_subs.removeListener('message', redis_listener);
});
});
根据上下文,您需要在 io.client.disconnect 事件上调用 redis.removeListener.
Depending on the context, you need to call redis.removeListener on io.client.disconnect event.
这是一个可能的替代方案:
Here's a probable alternative :
https://github.com/LearnBoost/Socket.IO/wiki/Configuring-Socket.IO
顺便说一下,这个相关问题似乎是一个有准确答案的类似问题,但实际上并非如此(不起作用).此外,请查看突出显示的评论:
By the way this related question seems a similar one with an accurate answer, but isn't actually (didn't work). Further more, look at the highlighted comment:
这篇关于Redis、Node.js 和 Socket.io:跨服务器认证和 node.js 理解的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!