[英]Authorize data access at database level
從問題標題中,您可能會猜到這是什么意思。 我將嘗試描述當前擁有的內容以及要存檔的內容。
假設一個應用程序處理四個實體: User , Team , Repository和Document 。 這些實體之間的關系是:
訪問用戶的文檔不是問題,這些都是存儲在他擁有的存儲庫中的所有文檔。 但是事情變得復雜了,因為我真正需要的是用戶可見的所有文檔,這就是所有文檔以及其他人公開並與他共享團隊的文檔。
目前,我正在數據訪問層中強制執行此授權機制。 這意味着要獲取所有文檔並按照上述規則進行一些過濾。 我知道此實現無法擴展,並且我想知道是否可以通過將授權邏輯移至數據庫來改善數據庫模型。 這樣,過濾將由數據庫引擎完成,並且只有請求的實體將返回給客戶端代碼。
這個問題與特定的實現無關,但我將其標記為正在使用的特定工具。 也許對某人的回答很有用。
首先,讓我解釋一下為什么使用實體框架(或另一個ORM工具)比使用存儲過程更優雅。
存儲過程是邪惡的 。 這就是為什么。 正如該鏈接詳細解釋的那樣,存儲過程傾向於作為第二個BL增長,因此難以維護。 當該列在多個存儲過程中使用時,重命名列這一簡單任務將成為一項艱巨的任務。 當您使用ORM工具時,Visual Studio將為您完成大部分工作。
話雖如此,但我卻獲得了實體框架的第二個優勢。 您可以使用自己喜歡的.net語言來組成查詢。 實體框架不會直接執行查詢。 您可以在此處控制查詢何時執行查詢。 執行此實體框架時,會將您的Linq語句編譯為完整的tsql語句,並針對數據庫運行該語句。 因此,絕對不需要獲取所有數據並遍歷每個記錄。
提示:將光標移到變量名上,ef將為您預覽將要編譯的TSQL語句。
那么您的Linq查詢應該如何? 我根據您的描述組成了一個測試數據庫,並為其創建了一個實體框架(ef6)模型,如下所示:
至少在我正確理解您的問題的前提下,此Linq查詢將執行您想要的操作。
private IEnumerable<Document> GetDocumentsOfUser(Guid userId)
{
using (var db = new DocumentRepositoryEntities())
{
// Get owned repositories by the user
var ownedRepositories = db.Repositories
.Where(r => r.Owner.UserId == userId);
// Get all users of teams the user belongs to
var userInOtherTeams =
db.Users.Where(u => u.UserId == userId)
.SelectMany(u => u.Teams)
.SelectMany(t => t.Users);
// Get the public repositories owned by the teammembers
var repositoriesOwnedByTeamMembers =
userInOtherTeams.Where(u => u.Repositories.Any())
.SelectMany(u => u.Repositories)
.Where(r => !r.Private);
// Combine (union) the 2 lists of repositories
var allRepositories = ownedRepositories.Concat(
repositoriesOwnedByTeamMembers);
// Get all the documents from the selected repositories
return allRepositories.SelectMany(r => r.Documents)
.Distinct()
.ToArray(); //query will be composed here!
}
}
請注意,在調用.ToArray()
時,linq語句將被編譯為TSQL select語句。
根據您的描述,目標是找到用戶當前有權訪問的所有存儲庫,然后從每個存儲庫中檢索文檔。
如果這是我的實現,則需要向數據庫中添加一個接受當前用戶ID的存儲過程,然后將可訪問存儲庫列表收集到本地表變量中,然后從documents表中選擇文檔存儲庫所在的位置。可訪問的存儲庫列表。
DECLARE
@Teams TABLE (TeamID UNIQUEIDENTIFIER NOT NULL PRIMARY KEY (TeamID))
DECLARE
@Repositories TABLE (RepositoryID UNIQUEIDENTIFIER NOT NULL PRIMARY KEY (RepositoryID))
/* Get the list of teams the user is a member of */
INSERT INTO @Teams
SELECT Teams.TeamID
FROM Teams INNER JOIN TeamUsers ON Teams.ID = TeamUsers.TeamID
WHERE TeamUsers.UserID = @UserID
/* Get the list of repositories the user shares a team member with */
INSERT INTO @Repositories
SELECT RepositoryID
FROM Repositories
WHERE OwnerID = @UserID
OR (OwnerID IN (SELECT DISTINCT TeamUsers.UserID
FROM TeamUsers INNER JOIN @Teams ON TeamUsers.TeamID = @Teams.TeamID)
AND IsShared = 1)
/* Finally, retrieve the documents in the specified repositories */
SELECT Documents.*
FROM Documents INNER JOIN @Repositories ON Documents.RepositoryID = @Repositories.RepositoryID
主管技術建議的答案是有效的,如果您的需求是一次性的,那么這很好,但理想情況下,您想要做的是在專用層中以外部化方式實現授權要求。 這樣做的原因包括:
要實現外部授權 (有關此主題的Gartner報告 ,請參見此處),您需要考慮基於屬性的訪問控制(ABAC- 有關NIST關於ABAC的報告,請參見此處 )和可擴展訪問控制標記語言(XACML)- 更多信息此處 )作為實施ABAC的一種方式。
如果遵循ABAC方法,您將獲得:
在上述示例中,用戶類型,資源類型(文檔),操作(查看,編輯),文檔團隊,用戶團隊和文檔可見性(私有或公共)都是屬性的示例。 屬性是生命線,ABAC的基礎。
ABAC可以輕松地幫助您實現從最簡單的要求到更高級的授權要求(例如可以在出口法規,合規性法規或其他業務規則中找到)。
這種方法的一個好處是它並不特定於數據庫。 您可以將相同的原理和策略應用於本地開發的應用程序,API,Web服務等。 這就是我所謂的外部授權的任何深度架構/方法。 下圖很好地總結了它:
PDP是您的集中式授權引擎。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.