1

这个问题的灵感来自this SO question about Access Control in ASP.NET MVC。在这里,我试图将公认的答案转化为切实的解决方案。

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

编辑:为这种方法添加了以下好处/理由

这种安全模型的灵感来自FileSystemRights,这是一种管理文件级别权限的 .NET 方法。

我喜欢这种方法的主要原因之一是我可以通过将所有单独的 ACL 组合在一起来轻松创建所有权限的摘要。我也喜欢我可以添加一个拒绝 ACL 来删除继承的权限。

List<myObject> 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;}

/结束编辑

enum然后我想到我可以通过来自更多特权用户的数值来比较特权较低的用户enum

我认为此定义将允许在 SQL 数据库中快速轻松地识别用户,而无需在后端解析枚举。(通过查找非特权用户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的权限表。这应该减少对特定行的并发更新的需要。 在此处输入图像描述

问题

  • 这是一个好主意吗?

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

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

4

2 回答 2

4

我会说不。

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

  2. 你假设那个特权0x1在逻辑上总是小于0x2等等0x80。根据我的经验,这种情况很少见。更常见的是,权限彼此完全独立(即:拥有“添加用户”权限与“上传图片”权限无关;一组用户(管理员)将拥有前者,而其他用户(内容发布者)拥有后者。这意味着您的数字比较并不像您最初想象的那么有用。

  3. 您正在做的事情可能会产生性能优势,但您还没有证明存在性能问题!我的大多数权限系统都使用每个用户一个数据库记录来授予或拒绝每个权限。获取 100 条记录不会对我的数据库造成负担。与使用位掩码相比,您最好在请求之间缓存每个用户的权限。

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

在尝试将大量数据打包到一个小空间时,我发现位掩码最有用。但是当我最终得到超过 64 件东西时,我的第 1 点常常会反过来咬我。

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

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

用户权限数据库图

(对不起我的用户的那些米奇老鼠耳朵!)

中的记录的存在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<Right>在我的系统中完成)。而且,如果您真的热衷于位掩码,您可以Dictionary<User, BitArray>在缓存中创建一个!

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

于 2011-11-02T22:19:16.713 回答
1

一般来说,将多个值(标志)放入单个字段是一个坏主意。

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

即使不做审计也可能遇到的问题

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

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

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

于 2011-11-02T21:12:32.427 回答