【问题标题】:Implement bitmask or relational ACL in PHP在 PHP 中实现位掩码或关系 ACL
【发布时间】:2012-08-03 16:35:04
【问题描述】:

我认为 PHP 人熟悉 E_ALL 和来自 error_reporting() 函数的各种其他位掩码常量。它们是数字常量,例如:E_ALL 表示 32676E_NOTICE 表示 8。我可以说我想要所有错误但显示通知,我通过传递E_ALL & ~E_NOTICE 作为error_reporting() 的参数来做到这一点。但本质上,我告诉它3275932767 - 8

这些位掩码从f(x) = 2^x 函数的输出集中获取它们的值,并对这些值进行加减运算,我们可以微调要得到的错误。

我正在考虑在我的框架中实现一个更可配置的访问控制。为此,我希望设置用户的位掩码将具有相同的将数值相加的方法,问题是我不知道如何实现这一点,如何检查请求的值和当前值:用户是否有权访问 foobar?.

另一个问题是可扩展性。我可能只有 31 个唯一位(因为2^32 达到了太大且无法维护的状态),如果我需要经历(现在还没有真正计划)这个障碍,那么迁移会很困难吗?我对访问控制的另一个想法是建立一个表,将user.id 和他拥有的访问位的整数值链接在一起。

总结一下,以下两个选项哪个是更好的解决方案?

  • 使用用户表中的一个字段来存储位掩码,然后在需要时从中获取请求的位。
  • 在表中定义一组访问标志,并拥有一个将用户链接到访问权限的关系表,利用关系数据库的强大功能满足我们的需求。

我正在研究第二种方法,但我不知道哪种方法最好。

【问题讨论】:

  • 为了清楚起见,您能否突出显示您的实际问题?
  • @Kristian 下面的两元素列表总结一下。对不起,我忘记了,现在已经完成了。
  • 您所描述的称为“位掩码”。 ;) 刚刚说过
  • @KingCrunch 我们每天都在学习新东西。谢谢。

标签: php mysql math acl bit-masks


【解决方案1】:

您永远不应该在数据库中使用代表多个不同值的大整数。每个字段一个值,这是数据建模(或其他东西)的第一定律。

换句话说,你应该有第二张桌子,说;一个用户 ID 字段和访问位。

用户权限:

  • 用户 ID,int,主要
  • 特权,诠释

【讨论】:

    【解决方案2】:

    位掩码

    位掩码的最初问题是它们违背了数据建模的惯例(以another answer here 表示,进一步阅读herehere)。一个 4 字节的有符号整数可能只包含 31 不同的值(以整数表示为 2 147 483 648),并且很难用它们进行计算。在讨论这个话题之前,Stack Overflow 上有各种各样的问题,我曾经用它来了解 bistmasks 的工作原理。

    测试还表明,使用位掩码很难。理解位运算符需要一种感觉,并且迁移基本上是不可能的。 (不可能我的意思是,起初,实现位掩码似乎是 good 要做的事情,但毕竟它需要 太多 花费与它可以带来的收益相比。)以一个基本的类似门户的网站...我的意思是不。以 Stack Overflow 为例,它有多少独特的特权。实际上,我曾尝试进行计数,但在复杂性中迷失了自己,但我们需要的数量非常接近,如果还没有超过 31 个唯一值的上述障碍的话。更改位掩码含义的项目更新很可能导致长期需要重新计算,并且如果遇到数据库错误,则更容易出错。

    虽然我没有准确、精确的时序数据,但使用位掩码感觉比 ACL 慢。比较数据库大小,内存和存储空间更大,我们利用关系数据库和索引功能的机会更少。对于网站上的用户权限,如果我们有其他可能使用的方法,位掩码是不行

    位掩码工作的系统有很多种,即MaNGOS(我最初是从这里提出这个想法的),item_template 和其他各种模板表定义了使用位掩码的flags。但底线是,在这些表中,值不可能永远改变,计算是只读,与用户任意获取和失去权限的网站相反。

    示例代码

    define('U_NIL', 0);
    define('U_EXEC', 1);
    define('U_WRIT', 2);
    define('U_READ', 4);
    
    $our_perm = 7;
    $req_perm = U_EXEC | U_WRIT | U_READ;
    var_dump( ($our_perm & $req_perm) == $req_perm ); # Will be bool(true)
    
    $our_perm = 3;
    var_dump( ... The same thing ...); # Will be bool(false)
    
    $our_perm = U_READ;
    $req_perm = U_READ | U_WRIT;
    var_dumo(...); # Will be bool(false)
    
    $our_perm = U_READ | U_WRIT | U_EXEC;
    $req_perm = U_READ;
    var_dump(...); # Will be bool(true)
    

    等等。

    我将省去你的代码行,因为variousotherquestions 恰当地描述了该方法,在某种程度上我永远无法描述它。位掩码看起来不错,位掩码似乎很奇特,但让它们在生产中安定下来是没有意义的。

    ACL

    原始问题中描述的另一个选项是利用关系数据库和 SQL 语言在数据库中设置权限。我们需要在数据库中再创建两个表:

    CREATE TABLE `perm_relation` (
        `row_id` int(10) NOT NULL AUTO_INCREMENT,
        `user_id` int(10) NOT NULL,
        `perm_id` int(10) NOT NULL,
        PRIMARY KEY (`row_id`),
        UNIQUE `permission` (`user_id`, `perm_id`)
    ) ENGINE=InnoDB;
    
    CREATE TABLE `permission` (
        `id` int(10) NOT NULL AUTO_INCREMENT,
        `name` varchar(64) NOT NULL,
        PRIMARY KEY (`row_id`)
    ) ENGINE=InnoDB;
    

    perm_relation.perm_id 将是指向permission.id 的外键,perm_relation.user_id 将是我们与users.id 的关系。在此之后,这只是我们如何组合编程逻辑的问题。

    SELECT row_id FROM perm_relation
    WHERE perm_id = (SELECT id FROM permission WHERE name = "U_SUPERADMIN")
    AND user_id = CURRENT_USER_ID;
    

    使用这种方法,我们可以实现更高的兼容性、更快的执行和更轻松的迁移。添加新权限只需要很短的时间,既可以定义新权限,也可以将一些权限授予任意用户。删除同样简单,只需要一个小的 SQL 查询来优化我们的表以删除孤立的整体(例如:具有权限的用户没有在系统中定义相关权限)。

    因为我们正在将该系统实现到 PHP 环境中,我们可以假设会有某种管理页面,其中包含许多依赖于该权限列表的功能。列出按他们拥有的权限过滤的用户的示例就是一个示例。在这种情况下,ACL 比位掩码要好得多,因为我们可以进一步利用 JOIN 语句……甚至更多。

    结论是位掩码和关系模式有不同的用途。对于用户权限系统而言,位掩码太多并且非常庞大,而在上面提到的 MaNGOS 示例中,关系表将是过度杀伤力。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-16
      相关资源
      最近更新 更多