【问题标题】:Socket.io the best way to store user authenticationSocket.io 存储用户身份验证的最佳方式
【发布时间】:2020-10-12 19:28:48
【问题描述】:

我想知道在 socket.io 中对用户进行身份验证的最佳方式是什么?我构建了一个聊天应用程序,将所有用户存储在这样的数组中:

let sockets = [];
sockets[userdbId] = socket.id

有一些问题。

io.on("connection", async socket => {
  
  socket.on("online", (userId, friends) => {
    sockets[userId] = socket.id;
    friends.forEach((id, index) => {
      if(sockets[id]){
        socket.to(sockets[id]).emit("friendOnline", data)}
    })
  })

  socket.on("Message", (message, userData, toUserId, callback) => {
    socket.to(sockets[toUserId]).emit("message",  {userId: userData._id, 
     userData: {userName: userData.userName,avatarUrl: userData.avatarUrl}, UserMessage: message })
      callback();
  })
})

-首先,当用户打开一个新选项卡时,react app 会与套接字建立新连接并生成不同的套接字 id,因此预览选项卡不起作用。

-第二,我的应用分为两部分。大厅和朋友聊天,我不确定但可能我以错误的方式实现它,因为朋友部分通过 io.connect(window.location.host) 连接到套接字并监听消息,但大厅部分连接 io(window.host) location.host/lobby/lobbyId) 所以这种连接再次更改套接字 id。

现在我正在寻找有关如何制作安全且唯一的套接字 ID 的解决方案。你知道如何解决吗?

【问题讨论】:

  • 我在考虑使用 jwt 令牌作为身份验证,您对此有何看法?
  • 为什么是数组?如果您的 userdbId 值变得非常大,这将无缘无故地创建一个巨大的数组。使用对象作为哈希:let sockets = { };
  • 只是临时解决办法,以后会用redis
  • 这是一个临时修复。如果您的 ID 值类似于 2930123 ,则绝对没有理由创建一个 huge 数组。 {} 的工作原理完全相同,但仅在必要时创建条目。除所需之外的零代码更改。

标签: node.js reactjs sockets socket.io


【解决方案1】:

我相信我在创建我的一个应用程序时遇到了类似的问题。

我解决这个问题的方法是通过添加 firebase 身份验证来唯一标识登录和退出我的网站的每个用户(基于电子邮件及其唯一的 USER uid)。因此,这可以防止 socket.id 的随机分配,并且可以更容易地在用户每次进入网站时识别用户。此外,您所说的第二个问题也可以通过身份验证来解决,以防止 socket.id 更改,因为可以根据其 USER uid 而不是从 socket.uid 识别用户。

以下是一些在实施身份验证时可能会有所帮助的文档:https://firebase.google.com/docs/auth/

希望这会有所帮助。

【讨论】:

  • 我忘了说我用的是MongoDB。
【解决方案2】:

根据您需要的全部功能,您可以考虑以不依赖每个用户的单个套接字连接的方式构建您的应用程序。相反,使用 socketIO 中间件和 JWT 之类的东西在连接时验证每个新连接并将其分配给用户。

在前端,一旦建立连接,您只需发送一个带有 JWT 令牌的授权数据包,在后端,您会收到该令牌,解析并验证它并 将 userId 存储为套接字的属性,而不是将套接字 Id 存储给用户。

socket.userId = userId;

这样,你后面所有的回调就可以只引用userId作为socket的参数了。

userId = socket.userId;

套接字实例在连接的整个生命周期内都存在,因此您不需要重新验证每条消息,但是如果您将令牌附加到每条消息并使用 socket.io 中间件来预处理每条消息,则可以:

  io.on('connection', (socket) => {

    // Socket Auth Middleware
    socket.use((packet, next) => {
    
      // Your authorization code goes here
      // and returns a userId if authorized
      
      // Attach the userId to the socket, which
      // will persist for the lifetime of the connection.
      socket.user = userId;
      
      // Pass to the next middleware, or to your socket 'routes'.
      return next();    
    });

   // Your socket 'online' and 'messages' callbacks here
  
   });

您需要 return next(); 将消息传递给您的路线,但当然您可以在没有 next() 的情况下直接返回以完全终止消息,如果验证失败。

优点:

  • 如果单个用户打开多个聊天窗口,“朋友”或“直接”或“两者”,他们应该可以正常工作,也无需在前端对消息进行排序。每个消息都会发送到相关的套接字。
  • 套接字有时会断开并重新连接。如果您的套接字配置为自动重新连接,则可以进行无缝恢复(假设您的套接字使用 JWT 之类的东西重新验证)。
  • 您可以使用 socket.io 房间将每个套接字订阅到相关房间,即:在大厅聊天中,您可以将连接的套接字订阅到大厅房间 ID,但您也可以将套接字订阅到用户的房间 (可以使用 userId,只要它不会与房间 ID 冲突)。这样,您可以仅将与用户相关的任何内容发送到所有用户的连接(即:可能是消息通知)。当然,您仍然可以使用 emit 直接向单个连接发送消息。

有一些方法可以尝试确保每个用户只有一个套接字,但我不确定在这种情况下这是否对您有益。

是否有任何理由这种方法可能不适合您?如果您有一些具体问题,我可以提供更多详细信息。

【讨论】:

  • 每次我从前端发出事件时看起来都不错,后端检查 jwt 是否正常。但是现在呢?如何实现向单个用户发出事件?
  • 用户 socket.io 房间:socket.io/docs/rooms-and-namespaces 任何时候你验证一个新的套接字连接,将该套接字添加到以 userId 命名的房间:socket.join(userId);然后,使用 IO.to(userId).emit(event, payload) 发送到那个房间。订阅该房间的所有套接字都将收到该消息。由于套接字可以订阅多个房间,因此也可以将其订阅到用户当前所在的任何聊天室。有意义吗?
  • 此外,您仍然可以仅使用 socket.emit() 直接发射到该套接字
猜你喜欢
  • 2012-08-19
  • 2013-08-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-21
相关资源
最近更新 更多