2014-03-12 71 views
4

我有一個快速應用程序啓動並運行,它使用Passport.js(帶有MongoDB後端)進行身份驗證。在Express應用程序中驗證Socket.IO客戶端

一切都很正常,但現在我面對的另一個挑戰:

我想有某種在我的項目「聊天」功能,併爲我使用Socket.IO(實時傳遞信息-時間)。

該頁面在加載之前驗證用戶是否已登錄,但仍然可以被繞過。

我希望Socket.IO流得到保護和授權。

如何將Socket.IO聊天系統集成到基於Passport.js的身份驗證中?

回答

3

我不太確定護照是如何工作的,但假設它在客戶端使用會話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客戶端在創建初始連接「升級」請求時附加到url作爲GET參數。
  4. 當socket.io接收到授權請求而不是驗證cookie時,它會解析url,將令牌作爲GET參數彈出,並嘗試針對由令牌鍵入的鍵值對查詢redis。如果redis查詢失敗或返回空數據,則拒絕授權請求。如果redis請求成功,那麼您可以完全訪問所有會話數據,現在您可以刪除redis中的鍵值對。

這裏可能存在的一個問題是,您需要強制socket.io客戶端在每次連接嘗試時強制一個新的連接。否則,它會嘗試使用相同的連接並因此使用相同的標記。如果您在驗證它們後刪除令牌,則會阻止套接字重新連接。根據您的應用程序,這可能是所需的行爲。這是爲我們的,所以我們保持原樣,但仍然值得了解。

該方法還使您的WS身份驗證機制獨立於您的WS庫。由於安全原因,Sockjs無法提供對連接請求的cookie訪問權限,所以這種方法使得我們從socket.io切換到sockjs也變得更容易。

0

如果您是根據快遞版本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')); 
    } 
});