ASP.NET MVC中的访问控制使用基于的Enum进行SQL中的int权限管理

这个问题的灵感来自这个关于 ASP.NET MVC中访问控制的SO问题 。 在这里,我试图将接受的答案带入一个切实的解决方案。

答案提到使用FileSystemSecurity作为管理权限的灵感。 这里我还使用带有Flags属性的枚举来定义所有对象的ACL。 此外,我的对象的每个成员都将存储在SQL中的一列中。 假设简化的Linq2SQL,EF或nHibernate ORM映射。

编辑:为此方法添加了以下好处/原理

此安全模型的灵感来自FileSystemRights ,即管理文件级权限的.NET方法。

我喜欢这种方法的主要原因之一是,我可以通过将所有单独的ACL进行OR运算来轻松创建所有权限的摘要。 我也喜欢我可以添加DENY ACL来删除inheritance的权限。

 List PermissionsList = GetACLForObject(ItemID, UserID); foreach (var acl in PermissionsList) { // The following enum has the [FlagsAttribute] applied so the .ToString() is pretty PermissionWithFlagsEnum sampleForSO = (PermissionWithFlagsEnum )acl.Permission; Console.Writeline ("Setting " + sampleForSO.ToString() + " permission for group: " + acl.ACLName); ResultingPermission = resultPermission | acl.Permission ; } public int ResultingPermission {get;set;} 

/结束编辑

然后我发现我可以通过来自更多特权用户的enumenum的数值来比较较少特权的用户。

我认为这个定义可以快速,方便地识别SQL数据库中的用户,而不必解析后端的枚举。 (通过select users from permissions where security < DBAceessRights.DetailRead )的select users from permissions where security < DBAceessRights.DetailRead查找非特权用户

这是我如何定义标志(最低值具有最少的权限)

 [Flags] public enum DBAccessRights { DenyAll =1 << 1, WikiMode = 1 << 4, AppearInListing = 1 << 8, DetailRead = 1 << 12, CreateNew = 1 << 18, DetailEdit = 1 << 22, DeleteChild = 1 << 26, DeleteThis = 1 << 30, EditPermissions = 1 << 31, } 

我有一个权限表 ,用于将用户ID连接到对象特定的ACE。 这应该减少对特定行的并发更新的需要。 在此处输入图像描述

  • 这是一个好主意吗?

  • 有人曾经做过(比这更好)吗? (编辑: 这是接受的答案 )

  • 如果这是实现权限的标准方法,它叫什么?

我会说不。

  1. 您假设您需要的权限不超过32(如果使用bigint则需要64)。 对我来说听起来像是一个任意的限制。 移动到数据库中的varbinary可以克服这个问题,但是你的枚举就在了小溪上(不能在byte[]上创建枚举)! 而且你将无法进行数字比较。

  2. 您假设特权0x1始终在逻辑上小于0x20x80 ,依此类推。 根据我的经验,这种情况很少见。 更常见的是,权限相互独立(即:具有“添加用户”权限与“上传图像”权限无关;一组用户(管理员)将拥有前者而其他用户(内容发布者)拥有后者。这意味着你的数字比较并没有你想象的那么有用。

  3. 你正在做什么可能会带来性能上的好处,但你还没有certificate存在性能问题! 我的大多数权限系统使用每个用户一个数据库记录来授予或拒绝每个权限。 获取100条记录不会对我的数据库征税。 您可以更好地简单地在请求之间缓存每个用户的权限,而不是使用位掩码。

  4. 关于更新性能:用户权限多久更改一次? 一旦系统到位,根据我的经验,它们往往是非常静态的。

我发现在尝试将大量数据打包到一个小空间时,位掩码最有用。 但是当我最终收到64件以上的东西时,我的观点经常会回来咬我。

请注意,我在记录用户操作的统计信息时使用了这种技术(跟踪用户在搜索中找到的项目,他们查看的实体等)。 我的理由纯粹是为了确保数据库记录长度固定且小,因此插入速度很快。 我没有做数字比较。 (而且,公平地说,我从未测试过int列和几个列之间是否存在任何差异)。

编辑一个基本的替代方案(我正在使用):用户和权限之间的M:N关系(我称之为权利)。

用户到权限DB图

(对不起我的用户那些Micky Mouse耳朵!)

UserRight存在记录表示授予该用户权限。 缺席表明没有权利。 此查询为您提供分配给用户的所有权限。

 SELECT [dbo].[User].Username, [dbo].[Right].Id, [dbo].[Right].Name FROM [dbo].[Right] INNER JOIN [dbo].[UserRight] ON [dbo].[Right].Id = [dbo].[UserRight].RightId INNER JOIN [dbo].[User] ON [dbo].[User].Id = [dbo].[UserRight].UserId WHERE [dbo].[User].Id = @pUserId 

然后,在代码中断言用户有权:

 var grantedRights = RunTheAboveQuery(currentUser.Id); if (grantedRights.Any(r => r.Id == requiredRight)) // User has the right. else // User does not have the right. 

显然,您可以对此进行扩展以检查用户在一个查询中是否具有多个权限。

这不会人为地限制您的系统支持的权限数量。 并且它不会假设权限之间存在任何关系(因此您无法进行数字比较;一切都由我的系统中的IEnumerable完成)。 而且,如果你真的热衷于位掩码,你可以在缓存中创建一个Dictionary

我还有一个Role的概念,它为用户提供了一组逻辑权利。 这只是另一个M:N表。 这是读者的练习!

通常将多个值(标志)放入单个字段是个坏主意。

如果您计划审核这些数据,那么这是一个非常糟糕的主意,因为它难以有效地梳理出哪些标志从更新更改为更新。

即使您不进行审核,您可能会遇到的问题

  • 许多简单查询(DeleteThis authroization的用户)不是SARGable,因为您需要在比较之前执行按位操作。

  • 还要select users from permissions where security < DBAceessRights.DetailRead可能无法返回正确的结果,因为AppearInListing & CreateNew大于DetailRead但没有打开DetailRead。 所以你希望的好处可能无法获得

  • 管理并发(多个ACL编写器)更加困难,因为改变一个“逻辑值”实际上是在改变所有值。