使用共享会话数据的socket.io身份validation,io.use()如何工作

受到如何与Socket.IO 1.x和Express 4.x共享会话的启发? 我以某种“干净”的方式实现了套接字authentication,不需要使用cookieparsing器,也不需要从头文件中读取cookie,但是对于我来说很less有东西不清楚。 示例使用上一个稳定的socket.io版本1.3.6。

var express = require('express'), session = require('express-session'), RedisStore = require('connect-redis')(session), sessionStore = new RedisStore(), io = require('socket.io').listen(server); var sessionMiddleware = session({ store : sessionStore, secret : "blabla", cookie : { ... } }); function socketAuthentication(socket, next) { var sessionID = socket.request.sessionID; sessionStore.get(sessionID, function(err, session) { if(err) { return next(err); } if(typeof session === "undefined") { return next( new Error('Session cannot be found') ); } console.log('Socket authenticated successfully'); next(); }); } io.of('/abc').use(socketAuthentication).on('connection', function(socket) { // setup events and stuff }); io.use(function(socket, next) { sessionMiddleware(socket.request, socket.request.res, next); }); app.use(sessionMiddleware); app.get('/', function(req, res) { res.render('index'); }); server.listen(8080); 

index.html

 <body> ... <script src="socket.io/socket.io.js"></script> <script> var socket = io('http://localhost:8080/abc'); </script> </body> 

所以io('http://localhost:8080/abc'); 从客户端发送初始HTTP握手请求到服务器,从服务器可以收集cookie和许多其他请求信息。 所以服务器可以通过socket.request访问这个初始请求。

我的第一个问题是为什么握手请求不在快速会话中间件的范围内?(更一般地在app.use中间件的范围内?)以某种方式,我期待这个app.use(sessionMiddleware); 在初始请求之前触发,然后轻松访问socket.request.session

其次,使用io.use()定义的中间件会触发什么样的场景? 仅用于初始HTTP握手请求? 好像io.use()用于套接字相关的东西 (问题是:什么东西 ),而app.use标准请求。

我不太清楚为什么在上面的例子中, io.use()io.of('/abc').use()之前被触发。 有意地我写了订单放io.of('/abc').use()第一次看到它会工作,它的工作。 应该是反过来写的。

最后, socket.request.res类似于也指向链接问题中的一些人, 有时undefined导致应用程序打破,问题可以通过提供空对象而不是socket.request.res来解决,如: sessionMiddleware(socket.request, {}, next); 这在我看来就像一个肮脏的黑客。 由于什么原因socket.request.res产生undefined

尽pipe@oLeduc是正确的,但还有一些事情可以解释..

为什么握手请求不在快速会话中间件的范围之内?

这里最大的原因是express中的中间件被devise为处理请求特定的任务。 不是全部,但大多数处理程序使用标准的req, res, next语法。 如果我能说,套接字是“无需请求”的。 你有socket.request的事实是由于握手的方式,并使用HTTP的。 所以socket.io中的那些人首先把请求放入你的套接字类,以便你可以使用它。 它并不是由快速团队devise的,以便与套接字和TCP协同工作。

使用io.use()定义的中间件会触发什么情况?

io.use是快速use中间件方式的紧密表示。 在快递中,中间件是执行每个请求,对不对? 但是套接字没有请求,在每个套接字上使用中间件会很尴尬,所以它们已经在每个连接上执行了。 但是,除了在实际请求处理(和响应)之前堆叠和使用快速中间件,Socket.IO使用中间件进行连接,甚至实际握手之前 ! 如果你愿意的话,你可以使用那种非常方便的中间件拦截握手(为了保护你的服务器免受垃圾邮件攻击)。 关于这个的更多信息可以在passport-socketio的代码中find

为什么io.use()在io.of('/ abc')。use()之前触发?

关于这个的真正解释可以在这里find,这是这个代码:

 Server.prototype.of = function(name, fn){ if (String(name)[0] !== '/') name = '/' + name; if (!this.nsps[name]) { debug('initializing namespace %s', name); var nsp = new Namespace(this, name); this.nsps[name] = nsp; } if (fn) this.nsps[name].on('connect', fn); return this.nsps[name]; }; 

在代码的开始,有这样的代码行:

 this.sockets = this.of('/'); 

所以,在一开始就有一个默认的命名空间。 就在那里,你可以看到它立即有一个connect到它的监听器。 之后,每个命名空间都会得到完全相同的connect监听器,但是由于NamespaceEventEmitter ,所以监听器被一个接一个地添加,所以它们会一个接一个地启动。 换句话说,默认的名字空间首先是监听器,所以它首先被触发。

我不认为这是故意devise的,但恰巧是这样:)

为什么没有定义socket.request.res?

说实话,我不太确定。 这是因为engine.io是如何实现的 – 你可以在这里阅读更多。 它连接到常规服务器,并发送请求以进行握手。 我只能想象,有时候错误的标题与响应分开,这就是为什么你不会得到任何。 无论如何,仍然只是猜测。

希望信息帮助。

为什么握手请求不在快速会话中间件的范围之内?

因为socket.io将附加到一个http.Server,它是快速下的图层。 在socket.io的源文件中提到了这一点。

原因是因为第一个请求是一个常规的http请求,用于将请求的无状态http连接升级到一个完全状态的websocket连接。 所以它必须通过所有适用于常规http请求的逻辑,这是没有什么意义的。

使用io.use()定义的中间件会触发什么情况?

每当创build一个新的套接字连接。

所以每次客户端连接时都会调用使用io.use()注册的中间件。 一旦客户端连接,但是从客户端收到数据包时不会被调用。 如果连接是在自定义命名空间或主命名空间上启动的,则无关紧要,它将始终被调用。

为什么io.use()在io.of('/ abc')。use()之前触发?

命名空间是socket.io实现的一个细节,事实上,websockets将总是首先命中主命名空间。

为了说明这种情况,请看这段代码和它产生的输出:

 var customeNamespace = io.of('/abc'); customeNamespace.use(function(socket, next){ console.log('Use -> /abc'); return next(); }); io.of('/abc').on('connection', function (socket) { console.log('Connected to namespace!') }); io.use(function(socket, next){ console.log('Use -> /'); return next(); }); io.on('connection', function (socket) { console.log('Connected to namespace!') }); 

输出:

 Use -> / Main namespace Use -> /abc Connected to namespace! 

看到socket.io团队添加到他们的文档的警告:

重要说明:名称空间是Socket.IO协议的实现细节,与底层传输的实际URL无关,默认为/socket.io/…。

为什么没有定义socket.request.res?

据我所知,它永远不应该是未定义的。 这可能与您的具体实施有关。