2016-08-02 63 views
-1

我們正在開發使用Azure DocumentDb作爲存儲的多租戶Angular Web App。來自應用程序所有租戶的文檔存儲在同一個集合中,並由文檔的tenantId屬性進行區分。我們將擁有中間層.NET服務,該服務持有DocumentDb的主密鑰並公開REST API端點以便通過Web App進行CRUD操作。 Web App的用戶通過oAuth提供商(Google,Facebook,Microsoft)進行身份驗證。確保租戶數據的最佳做法是什麼,以便一個租戶的用戶無法訪問其他租戶的數據?確保從多租戶SPA訪問DocumentDB

+0

不幸的是,這種類型的問題對於StackOverflow是無關緊要的,因爲沒有一種「最佳實踐」方式可以實現這一點。這是一個相當廣泛和意見徵求的問題,可能有很多答案。 –

回答

1

我沒有足夠的信心把它最好的做法,但我們有一箇中間層REST API像你一樣,在這裏我們做什麼:

  1. 客戶端(甚至瀏覽器代碼)可以提交任意查詢。我們使用sql-from-mongo,所以這些語言都是類似於mongo的語法,這對於javascript客戶端來說更容易構建查詢,但是我建議使用原始SQL查詢的安全性一樣安全,只要您將變量參數化並且禁止您的SELECT條款中的任何投影。這是我們在sql-from-mongo翻譯中完成的最後一部分,但是您可以刪除預測(將提供的SELECT子句替換爲SELECT *),或拒絕任何不以SELECT *開頭的查詢。這是非常重要的,因爲如果沒有它,不好的演員可以投射他們自己的tenentIDuserID,這些控制的其餘部分不起作用。

  2. 我們的REST中間件爲每個客戶端緩存租戶和用戶上下文,這些包含我們的授權規範(類似於Firebase的概念)。用最簡單的形式,用戶綁定到單個帳戶,並且該用戶已讀取該租戶的所有數據的權限,但您可以指定任何內容。

  3. 執行查詢時,將根據授權規範檢查返回的數據集。根據具體情況,任何數量的禁止文檔的請求都會被完全拒絕,或者不允許的文檔會從我們返回的結果集中過濾掉。

  4. 在寫事物的方面,我們做了類似的事情。我們根據授權規範檢查更新並拒絕任何不符合規定的更新。這比這更復雜一點,因爲我們在所有寫入中使用sproc,並且我們爲就地更新使用類似mongo的語法,並且我們爲所有寫入(使用sproc)實現了跨文檔ACID,但是您可以執行我沒有這些建議。

希望這會有所幫助。隨意在評論中提問。

+0

拉里,謝謝你的詳細回覆。我打算做的是: 1.使用oAuth提供程序登錄後,客戶端將oAuth提供程序ID發送到我們的Auth服務器。 2.Auth服務器從我們的數據庫中獲取相應的tenantId和userRole。 3.Auth服務器使用tenantId和userRole生成JWT並將其返回給客戶端。 4.客戶端將JWT包含到HTTP授權標頭中,用於對我們的REST API服務的每個請求 –

+1

我不建議您使用oauth標記作爲密鑰並僅將服務器緩存在服務器上將令牌傳遞給/從客戶端。否則,客戶可能會欺騙這些東西。 –

+1

或者,不要將其緩存在服務器上,而是在每個請求上查找授權信息。效率會降低,但可以讓服務器保持無狀態,並可能避免嚴重的緩存失效問題。 –

2

這可以通過在讀取&寫入時在邏輯上截取中間層中的請求以及在每個文檔中保留tenantId標記來實現。

讓我們從讀取路徑開始。假設每個文檔的tenantId屬性都設置爲該文檔所屬用戶的相應租戶。要讀取任何數據,請通過參數化SQL查詢(https://azure.microsoft.com/en-us/blog/announcing-sql-parameterization-in-documentdb/)並始終確保查詢具有tenantId過濾器。這將確保用戶的請求僅處理請求應該查看的租戶ID。參數化對於避免注入以及訪問其他租戶的數據非常重要。

在寫入路徑上,您需要確保每個文檔都具有正確設置的tenantId屬性。如果最終客戶端沒有設置,或者甚至沒有設置,則中間層應該解析並確保它與從OAuth提供程序返回的用戶授權令牌對應的租戶相匹配。

在此上下文中,請注意tenantId的分區鍵將有助於將單個租戶的所有數據放在一起。只要查詢具有tenantId過濾器,這對於在單個分區上進行置零方面的高效查詢很有幫助。