【问题标题】:Authenticate a Socket.IO client in an Express app在 Express 应用程序中验证 Socket.IO 客户端
【发布时间】:2015-01-06 23:08:51
【问题描述】:

我有一个 Express 应用程序启动并运行,它使用 Passport.js(使用 MongoDB 后端)进行身份验证。

一切都很好,但现在我面临另一个挑战:

我想在我的项目中加入某种“聊天”功能,为此我正在使用 Socket.IO(实时传递消息)。

页面在加载前会验证用户是否已登录,但仍然可以绕过。

我希望 Socket.IO 流也得到保护和授权。

如何将 Socket.IO 聊天系统集成到基于 Passport.js 的身份验证中?

【问题讨论】:

    标签: node.js authentication socket.io passport.js


    【解决方案1】:

    我不确定护照究竟是如何工作的,但假设它在客户端上设置了一个带有会话 ID 的 cookie,那么下面的解决方案应该可以工作。这看起来很老套,但我还没有看到更优雅的解决方案。

    var connect = require('express/node_modules/connect'),
        parseSignedCookie = connect.utils.parseSignedCookie,
        Cookie = require('express/node_modules/cookie'),
        store = YOUR_SESSION_STORE_INSTANCE, // i.e. redis-store, memory, or whatever 
        sessionKey = "YOUR SESSION KEY", // defaults to connect.sid
        sessionSecret = "YOUR SESSION SECRET";
    
    var verifyCookie = function(data, callback){
        try{
            var cookie = Cookie.parse(data.headers.cookie);
            var sessionID = parseSignedCookie(cookie[sessionKey], sessionSecret);
            store.get(sessionID, callback);
        }catch(e){
            callback(e);
        }
    };
    
    // set up socket.io to validate cookies on an authorization request
    // this assumes you've assigned your socket.io server to the io variable
    io.configure(function(){
        io.set("authorization", function(handshake, accept){
            if(handshake.headers.cookie){
                verifyCookie(handshake, function(error, session){
                    if(error || !session)
                        accept("Invalid authentication", false);
                    else
                        accept(null, true);
                });
            }else{
                accept("Invalid authentication", false);
            }        
        });
    });
    

    我在这里不能 100% 确定,但如果这不能正确获取会话,您可能需要将上面使用的会话存储更改为护照的会话存储,而不是默认的快速存储或您使用的任何内容。

    希望对您有所帮助。如果这可行,或者如果您找到正确的解决方案,请发布您的发现,因为我们将来也可能会迁移到护照,并且很高兴知道如何修补我们现有的代码。

    编辑:经过一番摸索后,看起来从 cookie 中获取会话数据后,如上所示,您必须找到用户 id 属性并将该数据传递给护照。他们在http://passportjs.org/guide/configure/ 显示的示例是:

    passport.deserializeUser(function(id, done) {
        User.findById(id, function(err, user) {
            done(err, user);
        });
    });
    

    编辑 2:另一种选择是完全回避这个问题。在实现上面列出的解决方案之前,我们走了一条不同的路线,其中涉及存储在 redis 中的一次性令牌。在高层次上,这个过程是这样工作的:

    1. 客户端首先通过常规 HTTP 请求从服务器请求令牌(实现为 uuid)。这通过 express 的中间件路由请求,并提供对所有会话数据的轻松访问。
    2. 生成令牌时,它作为键值对存储在 redis 中,其中令牌是键,会话数据是值。
    3. 令牌随后被传回给用户,socket.io 客户端在发出初始连接“升级”请求时将其作为 GET 参数附加到 url。
    4. 当 socket.io 收到授权请求而不是验证 cookie 时,它​​会解析 url,将令牌作为 GET 参数弹出,并尝试在 redis 中查询由令牌键入的键值对。如果 redis 查询失败或返回 null 数据,则您拒绝授权请求。如果 redis 请求成功,那么您可以完全访问所有会话数据,现在您可以删除 redis 中的键值对。

    这里的一个可能问题是您需要强制 socket.io 客户端在每次连接尝试时强制建立一个新连接。否则,它会尝试使用相同的连接并因此使用相同的令牌。如果您在验证令牌后删除令牌,这将阻止套接字重新连接。根据您的应用程序,这可能是所需的行为。它是给我们的,所以我们保持原样,但它仍然值得了解。

    这种方法还使您的 WS 身份验证机制独立于您的 WS 库。出于安全原因,Sockjs 不提供对连接请求的 cookie 的访问,因此这种方法也使我们从 socket.io 切换到 sockjs 更容易。

    【讨论】:

      【解决方案2】:

      如果您正在开发基于 Express 4.x 版和 Socket.io 1.x 版的应用程序,您可能需要阅读这篇文章: http://mykospark.net/2014/07/authentication-with-socket-io-1-0-and-express-4-0

      在我的例子中,身份验证过程的代码如下所示:

      io.use(function(socket, next) {
          var handshake = socket.handshake;
      
          if (handshake.headers.cookie) {
              var req = {
                  headers: {
                      cookie: handshake.headers.cookie,
                  }
              }
      
              cookieParser(config.session.secret)(req, null, function(err) {
                  if (err) {
                      return next(err);
                  }
                  var sessionID = req.signedCookies[config.session.name] ||
                                  req.cookies[config.session.name];
      
                  var sessionStore = new MongoStore({ db: global.db});
                  sessionStore.get(sessionID, function (err, session) {
                      if (err) {
                          return next(err);
                      }
      
                      // userData bellow is written once the Express session is created
                      if (session && session.userData) { 
                          next();
                      } else {
                          return next(new Error('Invalid Session'));
                      }
                  })
              });
          } else {
              next(new Error('Missing Cookies'));
          }
      });
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-02-02
        • 2022-08-19
        • 2020-10-14
        • 2015-11-03
        • 1970-01-01
        相关资源
        最近更新 更多