精华 简要分析socketio-auth
发布于 2天前 作者 luoyjx 194 次浏览 来自 分享

为什么写这篇

看到不少noder在问socket.io如何控制权限这类问题,想到去年在echojs还是HackerNews上看到的socketio-auth模块,还是一定程度上解决这类问题的,尽管它的实现相当简单,但至少提供了一个思路。

socketio-auth是啥

原文在这里《socket.io认证模块socketio-auth》 估计有同学之前没见到这个模块,我还是把之前写的原文贴过来,思路也连续一点。

介绍

该模块提供了钩子,实现了socket.io认证。无需使用查询字符串发送凭据,这不是一个良好的安全习惯。

默认情况下它通过标记客户的未认证的并监听的认证事件。如果客户端提供了错误的凭据或不验证它就会断开。当服务器等待连接的客户端进行身份验证,也不会发射任何事件。

用法

要为socket.io连接设置身份验证,只需将服务器套接字和一个配置对象传给socketio-auth: 

    var io = require('socket.io').listen(app);
    
    require('socketio-auth')(io, {
      authenticate: authenticate, 
      postAuthenticate: postAuthenticate,
      timeout: 1000
    });

所支持的参数是:

authenticate:唯一需要的参数。这是一个函数,它接受客户端发送的数据,并调用一个回调表明,如果认证是成功的:

    function authenticate(data, callback) {
      var username = data.username;
      var password = data.password;
    
      db.findUser('User', {username:username}, function(err, user) {
        if (err || !user) return callback(new Error("User not found"));
        return callback(null, user.password == password);
      }
    }

postAuthenticate:在客户端进行身份验证后才能调用的一个函数。对保持用户和客户端socket之间的联系是非常有用的:

    function postAuthenticate(socket, data) {
      var username = data.username;
    
      db.findUser('User', {username:username}, function(err, user) {
        socket.client.user = user;
      }
    }

timeout:客户端断开连接之前进行身份验证等待的毫秒数。默认值是1000。 
客户端只需要确保在连接后进行验证: 

    var socket = io.connect('http://localhost');
    socket.on('connect', function(){
      socket.emit('authentication', {client: "John", password: "secret"});
    });

该服务器将发射的已认证事件,以确认认证。

大致验证过程

1.为socket.io挂上模块

var io = require('socket.io').listen(app);
    
require('socketio-auth')(io, {
    authenticate: authenticate, 
    postAuthenticate: postAuthenticate,
    timeout: 1000
});

参数

  • authenticate 验证用户身份,比如在这里查询数据库,对比用户信息,成功后标记,然后调用下面的postAuthenticate函数
  • postAuthenticate 在验证成功后的处理,你可以做一些你爱做的事,比如买衣服什么的:)
  • timeout 超时时间,等待用户验证的时间毫秒数,超过这个时间就会强制断开了

在这个过程干了这么几件事:

这里主要是注册事件做准备工作。

2.客户端连接并进行验证

客户端在连接后,emit一个authentication事件并附带验证信息。 这时就会进入到验证流程里了,首先将信息传入authenticate方法里进行诸如查DB的验证过程。

//验证
config.authenticate(data, function(err, success) {
    if (success) {
      debug('Authenticated socket %s', socket.id);
      socket.auth = true;

      ...
    } else if (err) {
      ...
    } else {
      ...
    }

});

//验证的实现
function authenticate(data, callback) {
  var username = data.username;
  var password = data.password;
  
  db.findUser('User', {username:username}, function(err, user) {
  if (err || !user) return callback(new Error("User not found"));
    return callback(null, user.password == password);
  }
}

回调时看是否成功,成功就可以对连接进程标记了(稍后就不会被强制断开)。

成功时会调用postAuthenticate函数,比如将user信息绑定到连接或者其他的什么,总之,这个不是必须的。

3.返回状态给客户端

成功

socket.emit('authenticated', success);

失败

//error
socket.emit('unauthorized', {message: err.message}, function() {
    socket.disconnect();
});

//fail
socket.emit('unauthorized', {message: 'Authentication failure'}, function() {
    socket.disconnect();
});

后续任由客户端发挥了。

4.服务端还有一件小事要完成

那就是在timeout时间过后判断是否已验证,否则强制断开连接。

setTimeout(function() {
  // If the socket didn't authenticate after connection, disconnect it
  if (!socket.auth) {
    debug('Disconnecting socket %s', socket.id);
    socket.disconnect('unauthorized');
  }
}, timeout);

完…

https://github.com/invisiblejs/socketio-auth

追加

@Pana 分享了另外两个也一并列出,有待研究 socketio-jwt session.socket.io

11 回复

好东西,这个还没研究过

From Noder

这个认证是明文传递吧。可以走https吗?

@leapon 额,暂时还没有试过

@leapon 找了个案例,看上去socket.io的https似乎还是得依赖https server

https://github.com/a1arias/express-socketio-https-setuid-skel/blob/master/bin%2Fwww

原本没看明白,一点开原文就看明白来。。。。

@captainblue2013 额,这篇有哪些地方没写明白的么

@captainblue2013 把之前的原文贴过来了

用socketio来实现权限控制,没试过这样做,但是试过在socketio中通过识别session来实现权限控制,用的是express-socket.io-session。地址是 express-socket.io-session 觉得还不错。

@kiroChen 可以发个贴分享下哈~

回到顶部