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

发布于 2024-12-13 11:57:24 字数 2195 浏览 0 评论 0 原文

这个问题的灵感来自this SO question关于ASP.NET MVC中的访问控制。在这里,我试图将公认的答案转化为切实可行的解决方案。

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

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

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

我喜欢这种方法的主要原因之一是,我可以通过将所有单独的 ACL 组合在一起来轻松创建所有权限的摘要。我还喜欢可以添加 DENY 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;}

/End Edit

然后我想到,我可以通过特权用户的enumenum 数值来比较特权较低的用户。

我认为这个定义将允许快速、轻松地识别 SQL 数据库中的用户,而无需在后端解析枚举。 (通过从安全性的权限中选择用户来查找非特权用户)

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

[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。这应该会减少对特定行的并发更新的需要。 在此处输入图像描述

问题

  • 这是一个好主意吗?

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

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

This question is inspired by this SO question regarding Access Control in ASP.NET MVC. Here I'm trying to bring the accepted answer into a tangible solution.

The answer mentions using FileSystemSecurity as an inspiration to managing permissions. Here I'm also using an enum with Flags attribute to define the ACL for all my objects. Additionally each member of my objects will be stored in a column within SQL. Assume a simplified Linq2SQL, EF, or nHibernate ORM mapping.

Edit: Added the following benefit / rationale for this approach

This security model was inspired by the FileSystemRights, the .NET approach to managing file level permissions.

One of the main reasons I like this approach is so I can easily create a summary of all the permissions by OR'ing all the individual ACLs together. I also like that I can add a DENY ACL to remove an inherited permission.

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

/End Edit

Then it occurred to me that I could compare less privileged users by numeric value of the enum from more privileged users's enum.

I'm thinking that this definition would allow for quick and easy identification of users within the SQL database, without having to parse the enum on the backend. ( Find unprivileged users via select users from permissions where security < DBAceessRights.DetailRead )

Here is how I defined the flags (lowest value has the least permission)

[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,

}

I have a permission table that joins a user ID to an object specific ACE. This should reduce the need for concurrent updates on a particular row.
enter image description here

Question

  • Is this a good idea?

  • Has someone done it before (better than this)? (Edit: It is the accepted answer here )

  • If this is a standard way of implementing permissions, what is it called?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

满地尘埃落定 2024-12-20 11:57:24

我会说不。

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

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

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

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

I'd say no.

  1. You're assuming you'll need no more than 32 (or 64 if you use bigint) privileges. Sounds like an arbitrary limit to me. Moving to a varbinary in the database can overcome this, but then your enum is up the creek (can't create enums on byte[])! And you won't be able to do numeric comparisons.

  2. You're assuming that privilege 0x1 will always be logically less than 0x2 and 0x80 and so on. That's rarely the case in my experience. More usually, privileges are quite independent of each other (ie: having the "add user" privilege has nothing to do with the "upload image" privilege; one group of users (admins) will have the former while others (content publishers) have the latter. This means your numeric comparison isn't as helpful as you first thought.

  3. What you're doing may yield a performance benefit, but you haven't demonstrated there's a performance problem yet! Most of my privilege systems work with one database record per user to grant or deny each privilege. Fetching 100 records doesn't tax my database. You'd do better simply caching each user's permissions between requests than using a bitmask.

  4. Regarding update performance: how often do user permissions change anyway? Once a system is in place they tend to be pretty static in my experience.

I've found bitmasks most useful when trying to pack alot of data into a small space. But often my point 1 comes back to bite me when I end up with more than 64 things.

Note that I have used this technique when recording statistics of user actions (tracking what items users find in searches, what entities they view, etc). My reason was purely to make sure database record lengths were fixed and small so inserts were fast. And I wasn't doing numeric comparisons. (And, to be fair, I never tested to see if there was any difference between an int column and several bit columns).

EDIT
A basic alternative (which I'm using): an M:N relationship between Users and Privileges (I call them Rights).

Users To Rights DB Diagram

(Sorry about those Micky Mouse ears on my user!)

The presence of a record in UserRight indicates the right is granted to that user. The absence indicates no right. This query gives you all the rights assigned to a user.

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

Then, in code to assert a user has a right:

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

Obviously, you can scale this to check a user has several rights in one query.

This doesn't artificially limit how many Privileges your system supports. And it doesn't assume any relationships between Privileges (so you can't do your numeric comparisons; everything is done by IEnumerable<Right> in my system). And, if you're really keen on bitmasks, you could create a Dictionary<User, BitArray> in a cache!

I also have the concept of a Role which provides a logical group of rights for users. It's just another M:N table. That's an exercise for the reader!

空袭的梦i 2024-12-20 11:57:24

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

如果您计划审核这些数据,那么这是一个非常糟糕的主意,因为它很难有效地找出哪些标志在更新之间发生了变化。

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

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

  • 此外从安全性 的权限中选择用户
    DBAceessRights.DetailRead
    可能不会返回正确的结果,因为
    出现在列表中& CreateNew 大于 DetailRead 但不大于
    打开 DetailRead。因此,您希望获得的好处可能无法获得

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

In general putting multiple values (flags) into a single field is a bad idea.

If you're planning on auditing this data its a really bad idea since it because its hard to efficiently tease out which flags changed from update to update.

Problems you may encounter even if you don't do auditing

  • Many simple queries (what users the DeleteThis authroization) aren't
    SARGable because you'll need to perform a bitwise operation before
    a comparison.

  • Also select users from permissions where security <
    DBAceessRights.DetailRead
    may not return the right results because
    AppearInListing & CreateNew is greater than DetailRead but doesn't
    have the DetailRead turned on. So the benefit you hoped for you may not get

  • Managing concurrency (multiple writers to ACL) is more difficult since mutating one "logical value" is actually mutating all the values.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文