Socket.io 中的身份验证

Posted

技术标签:

【中文标题】Socket.io 中的身份验证【英文标题】:Authentication in Socket.io 【发布时间】:2015-08-29 03:04:51 【问题描述】:

我将尝试验证 socket.io 上的连接。

目前,用户首先通过 REST API 进行身份验证,然后,我向用户发送 JsonWebToken 以及经过身份验证的用户的用户名。在我打开客户端和服务器之间的连接后,我的计划是暂时从连接的套接字列表中删除该套接字,以防止在我执行身份验证时在服务器之间接收和发送数据。

在此身份验证中,我验证令牌,如果令牌有效,我将套接字的 ID 重新添加到连接的套接字列表中。唯一的问题是第一部分不起作用。我似乎无法从列表中删除套接字。

为了测试这一点,我做了以下操作。

io.on('connection', function(socket)
    //temp delete socket
    delete io.sockets.connected[socket.id];
    console.log(io.sockets.connected);
    socket.emit("test");
);

如您所见,我删除了套接字并发出一个测试事件以查看套接字是否仍然打开。客户端收到了不应该收到的消息。

有人知道为什么会这样吗?

【问题讨论】:

如果套接字仍然打开。然后它总是在监听。在此之前你应该确认所有连接都已关闭。如果其中任何一个未关闭,则实时客户端将收听。谢谢。 您不想暂时断开套接字。 socket.io 有一个您可以插入的身份验证方案,这样如果套接字没有进行身份验证,就永远不会接受它。请记住,所有 webSocket 连接都以 http 请求开始,因此如果您已经通过 http 进行了身份验证,您也可以将其用于 webSocket。同样从io.sockets.connected 中删除套接字不会断开套接字。 我使用 rest api 对用户进行身份验证,以便他们可以使用该服务。据我所知,我可以对用户进行身份验证,但无法以相同的方式对 socket.io 会话进行身份验证。我的想法是使用令牌来确保使用套接字的人是经过身份验证的人 见this article 的例子。如果你在其余 api 被认证的时候设置了一个 cookie,那么你可以在 socket.io 中间件中访问那个 cookie 来确定是否通过了认证。或者,您可以从 socket.io 中间件启动一个单独的身份验证进程。 socket.io 内置了访问 http cookie 或执行您自己的身份验证的功能,因此您在执行身份验证时不必断开连接,您可以将身份验证作为连接过程的一部分。 让我创建一个示例,如何使用与 http 服务器相同的令牌来验证套接字客户端。 【参考方案1】:

socket.io 还将套接字保存在命名空间中(如果未指定,则为默认值),您需要将其删除以停止接收消息。

请参阅 this post 了解您正在尝试执行的操作的分步说明,并参阅 this module 抽象整个过程。

【讨论】:

【参考方案2】:

尝试使用套接字对象的断开方法,如下所示:

io.on('connection', function(socket)
    //temp delete socket
    socket.disconnect();

    console.log(io.sockets.connected);
    socket.emit("test");
);

更新:

例如,如果您的 HTTP 服务器给客户端一个令牌:

app.post('/api/users', function (req, res) 
  var user = 
    username: req.body.username
  ;

  var token = jwt.sign(user, secret, expiresInMinutes: 30);

  res.json(token: token);
);

然后您可以重复使用该令牌来验证您的 websocket 连接。

来自客户端的令牌发送代码(html文件)将是:

socket = io.connect('http://localhost:4000', 
  query: 'token=' + validToken,
  forceNew: true
);

服务器(socketio)中的socketio授权码为:

// here is being used a socketio middleware to validate
// the token that has been sent
// and if the token is valid, then the io.on(connection, ..) statement below is executed
// thus the socket is connected to the websocket server.
io.use(require('socketio-jwt').authorize(
  secret: secret,
  handshake: true
));



// but if the token is not valid, an error is triggered to the client
// the socket won't be connected to the websocket server.
io.on('connection', function (socket) 
  console.log('socket connected');
);

请注意,在 express 上用于生成令牌的秘密,在 socketio 中间件的验证令牌上也使用了相同的令牌。

我创建了一个示例,您可以在其中看到这种验证是如何工作的,源代码在这里:https://gist.github.com/wilsonbalderrama/a2fa66b4d2b6eca05a5d

将它们复制到一个文件夹中并使用 node 运行 server.js,然后通过以下 URL 从浏览器访问 html 文件:http://localhost:4000

但首先安装模块:socket.io、express、socketio-jwt、jsonwebtoken

【讨论】:

身份验证完成后如何重新连接?会不会像 socket.reconnect() 一样简单? 认证前无需断开连接。 socket.io 有认证中间件。一个人可以直接插入它,并且套接字在经过身份验证之前不会首先连接。 @WilsonB​​alderrama 我确实尝试过这个,但我无法通过这个 ios 框架让它工作。 github.com/socketio/socket.io-client-swift。问题是我每次都会收到没有发送标头的错误。 @AlexCatchpole 我对 iOS 没有任何经验,但您是否使用此 method 进行连接?我认为应该是 "connectParams": [ "token": userToken ] @WilsonB​​alderrama 感谢您的帮助,非常感谢。

以上是关于Socket.io 中的身份验证的主要内容,如果未能解决你的问题,请参考以下文章

Laravel 5:使用 Laravel 会话数据的 Socket.io 客户端身份验证

在 socket.io 上,我是不是总是必须对每个 emit() 进行身份验证,而不仅仅是在建立连接时进行身份验证?

socket.io 身份验证与共享会话数据,io.use() 如何工作

在建立连接之前使用后备传输进行 Socket.IO 身份验证

我可以通过身份验证与 HAProxy 和 socket.io 进行粘性会话吗?

无法让 JWT 身份验证与 socket.io 一起使用