Socket.io基于Token的认证

作者好牛逼,我不懂的他全懂了。

Token-based Authentication with Socket.IO

简介

在实时框架中做授权是个比较大的挑战。也许这是因为他们的工作方式和普通的web应用完全不一样。其风险在于,如果没有正确的做认证,你有可能从其他用户信息流嗅探到信息。socket 服务器不会自动的知道哪个用户登录了,因此所有的人都可以加入流。

下面这张图显示了常有的错觉:

在此输入图片描述

其错误在于,觉得在web应用上已授权用户,同样的在 socket 流中被授权。但是这里是两个完全不一样的频道。

Cookie-based 和 Token-based 授权

这里有两种方式处理这种问题:传统的 cookie-based 方式和 token-based 方式。下图显示了这两种方式的工作流:

在此输入图片描述

在我们前面的文章里,我们写了用 token-based 方式的好处:Cookies vs Tokens. Getting auth right with Angular.JS。下面是对在实施框架使用 cookie 的一些思考:

  • 耦合

用 cookie 面临的第一个问题就是要把 web 应用授权机制和 socket 服务授权机制耦合起来。这在一些情况下当然OK,但是也就意味着你被绑在了特定的认证框架上。比如说 web 框架处理 session cookie,而你就没那么容易的做处理,你必须理解它内部实现。

  • 很难配置

配置很容易出错。一年前,我写了一个 passport-socketio,基本上解决了在 express session 中保存用于 socket.io 的认证的 passport 信息。蛋面临这样的情况,很多不熟悉 passport.js 的用户会发生这样那样的问题。

  • 不支持通用设备 如果你是从某些没有 cookie 的终端访问 socket 的话(好吧,有是有,可是你得访问到他们),因此你得为不同的设备提供不同的实现。

  • 需要 session 配置

你要依赖一个 session 存储(比如 Mongodb, Redis, 或者存在 cookie中)。

  • PHP + Socket.IO

用 node 取得 PHP 的 cookie 和 session 非常简单。这篇 blog 做了很好的说明。在别的技术(django,java, 等...)上,一样面临了同样的问题。

用 Token 认证 Socket

那到现在,你应该不会再觉得,我不用 cookies 是件很不可思议的事情了。用 token,就这样愉快的决定了。让我们看这样一个简单的例子,用 expresssocket.io。以及用 JWT 来处理授权。

服务端

直接上代码,注意 /loginsocketioJwt.authorize 的用法。

<!-- lang: js -->
var jwt = require('jsonwebtoken');
// other requires

app.post('/login', function (req, res) {

  // TODO: validate the actual user user
  var profile = {
    first_name: 'John',
    last_name: 'Doe',
    email: 'john@doe.com',
    id: 123
  };

  // we are sending the profile in the token
  var token = jwt.sign(profile, jwtSecret, { expiresInMinutes: 60*5 });

  res.json({token: token});
});

var server = http.createServer(app);

然后 socket.io 服务器

<!-- lang: js -->
var socketioJwt = require('socketio-jwt');

var sio = socketIo.listen(server);

sio.set('authorization', socketioJwt.authorize({
  secret: jwtSecret,
  handshake: true
}));

sio.sockets
  .on('connection', function (socket) {
     console.log(socket.handshake.decoded_token.email, 'connected');
     //socket.on('event');
  });

server.listen(9000, function () {
  console.log('listening on http://localhost:9000');
});

JWT 通过 jwtSecret 署名,存在服务器上。

这里我们使用 socket.io 的 全局认证回调。我们用一个简单的模块(socketio-jwt) 帮我们处理 JWT 的细节。这个模块希望在握手的时候,能够拿到 querystring 里面的 JWT 。

如果服务端发送一个认证 JWT, 然后握手成功, connection 事件就会被触发了。

客户端

一个简单 javascript 客户端的代码:

<!-- lang: js -->
function connect_socket (token) {
  var socket = io.connect('', {
    query: 'token=' + token
  });

  socket.on('connect', function () {
    console.log('authenticated');
  }).on('disconnect', function () {
    console.log('disconnected');
  });
}

$('#login').submit(function (e) {
  e.preventDefault();
  $.post('/login', {
    username: $('username').val(),
    password: $('password').val()
  }).done(function (result) {
    connect_socket(result.token);
  });
});

就像前面说的,这比用 cookies 和 session 要简单多了。并且它非常容易跨技术实现。

完整的代码在这里

你可以在 WebSockets 上用非常类似的方式实现,代码在这里

相关文章
相关标签/搜索