【问题标题】:Fine-grained access control细粒度的访问控制
【发布时间】:2024-01-30 09:55:01
【问题描述】:

我熟悉为我们正在开发的基于 Web 的管理应用程序对用户进行身份验证的一大堆方法,甚至还有各种密切关注授权的技术...

但是,我的问题是,您如何建议我实施提供以下功能的细粒度访问控制机制:

  1. 用户属于“角色”或“组”,例如“销售人员”、“计划”等。
  2. 管理菜单系统仅显示具有与用户角色相关的功能的“页面”
  3. 这些页面中的特定功能有限制 - 例如,在“新预订”页面上,“销售人员”用户可以“仅在将来”发出预订,而在“编辑预订”页面上可以编辑预订“一个”从现在开始的一周'。但是,“计划”用户可能被允许追溯预订“最多一周前”并编辑自己在“任何时间段”进行的预订,但其他人只能在“直到明天”进行预订...

我知道我可以实现一个基本的基于角色的系统来满足第一...我有一种感觉我应该将整个应用程序分成代码块,每个代码块都有自己的 objectID-permissionID 关系,这样我就可以扫描权限数据库,以查看哪些对象可用 - 这将有助于我的第 2 项。

我如何构建表单控件的任何想法,例如,对于“销售”用户,它只显示未来的日期(但为计划用户显示最多“一周前”的日期),然后以某种方式将其与POST 解析器中检查日期是否在预期范围内的行?

我已经尝试过将每个代码块保存到数据库中的想法,然后有一个对象表,它根据权限表动态构建代码,这样服务器上唯一的“文件”就是数据库连接文件!

欢迎任何想法...(即使您的背景不是 php/MySQL)


从 Zed Shaw 的 CUSEC 演示中更深入地了解该问题,他谈到了“ACL 已死”的原因 - http://vimeo.com/2723800

【问题讨论】:

    标签: php mysql authorization access-control


    【解决方案1】:

    警告,前面还有很多 Zend Framework!

    您可以使用 Zend_AclZend_Navigation 轻松处理 1. 和 2.。

    对于第 3 项,您将不得不查询模型中的 ACL 对象并手动执行很多操作。您也可以将 Zend Framework 用于表单,并根据用户角色权限包含特定的表单元素验证器。

    编辑:

    如果你不想走 ZF 路线,你至少可以看看 ZF 是如何处理 ACL 的。

    【讨论】:

    • 嗨,Goran,感谢有关 Zend ACL 的建议 - 我已经阅读了很多 Zend 文档!然而,由于我们需要实现我们自己的“本机”方法,我试图找到 Zend ACL 如何集成到其他应用程序中的示例,以便我们可以借鉴一些想法......请参阅下面的另一个想法!
    【解决方案2】:

    如果您想构建真正的细粒度访问控制 (FGAC),请查看我关于 MySQL 的关于此主题的文章:

    MySQL 5.0 Fine-Grained Access Control (FGAC)

    基本上,您不希望您的业务代码依赖于 FGAC 实现,您不希望在业务规则的 select 语句的 where 子句中混合 FGAC 代码。 本文介绍了避免 SQL 语句混乱的解决方案。

    【讨论】:

      【解决方案3】:

      为了实现“原生”​​方法,而不是捎带框架,我一直在尝试以下方法。有人会评价这种方法吗?您预见到任何陷阱吗?

      // Check database for existence of this $user against this $object.
      function get_permission($user, $object){
          // Query goes here...
          if( ... ){
              return $permission;
          } else {
              return FALSE;
          }
      }
      

      上述函数将查询数据库并输出如下内容:

      // Result of role-object query.  
      role_ID      object_ID          permission  
      -------      ---------          ----------
      salesperson  new_booking_date   'min' => 'now', 'max' => '+1 year'  
      planning     new_booking_date   'min' => '-1 week', 'max' => '+1 year'  
      salesperson  edit_booking_date  'this_user_min' => 'now', 'this_user_max' => '+1 week', 'other_user_min' => 'now', 'other_user_max' => '+1 week'  
      planning     edit_booking_date  'this_user_min' => '-1 week', 'this_user_max' => '+1 year', 'other_user_min' => '-1 week', 'other_user_max' => '+1 week'  
      

      包含表单输入的页面中的以下代码:

      // Draw form control with javascript date validation...
      $this_permission = get_permission($this_user, 'new_booking_date');
      if($this_permission){
          $html->datepicker('min' => $this_permission['min'], 'max' => $this_permission['max']);
      }
      

      预订完成后,另一个页面允许我们编辑该字段:

      // Verify POST data...
      $this_permission = get_permission($this_user, 'edit_booking_date');
      if($this_permission){
          if($this_user == $author_user && $_POST['date'] >= strtotime($this_permission['this_user_min'], $date_ref) && $_POST['date'] <= strtotime($this_permission['this_user_max'], $date_ref)){
              // Update database...
          } elseif($_POST['date'] >= strtotime($this_permission['other_user_min'], $date_ref) && $_POST['date'] <= strtotime($this_permission['other_user_max'], $date_ref)){
              // Update database...
          }
      }
      

      我在赛道上吗?

      【讨论】:

      • 这看起来很像 ZF acl 组件,只是添加了权限规范,只是您合并了操作和资源。这样,您将无法向资源添加继承,但您似乎不需要它,目前我认为资源继承无论如何都是一个坏主意。权限看起来很有趣,但却是静态的。 E.i.它们与特定规则相关联。
      • 我不喜欢的东西是 $this_user == $author_user。如果我正确理解您的示例,这实际上是一个硬编码规则:如果用户是作者,则允许访问。像这样的检查属于 acl。
      • 谢谢科恩。据我了解,我仍然可以添加继承(因为这是我热衷于实现的功能),但症结在于您提到的“硬编码”规则 - 通常,我不介意坚持用户流控制在代码中(因为它不是严格的授权;它更像是身份验证......)但我很想知道您是否知道如何将这样的规则分离到 ACL 中?塔你的意见!
      • 我不喜欢接受我自己的答案,但对于任何人来说,这就是我们正在使用的!
      【解决方案4】:

      我开发了一个名为PHP-Bouncer 的库,我认为它可以很好地满足您的需求。它目前支持完全托管的访问,这将允许您在每个页面上使用单个调用(我当然建议使用包含)并在人们无权访问页面时自动重定向,以及自动检索角色一个数据库(如果您使用包含的 MySQL 表设置脚本在数据库中实现角色)。语法很简单。

      您创建保镖:

      $bouncer = new Bouncer();
      

      添加您的角色(手动):

      // Add a role     Name,      Array of pages role provides
          $bouncer->addRole("Public", array("index.php", "about.php", "fail.php"));
      // Add a role          Name,              Array of pages role provides
          $bouncer->addRole("Registered User", array("myaccount.php", "editaccount.php", "viewusers.php"));
      // Add a role          Name,   Array of pages role provides       List of pages that are overridden by other pages
          $bouncer->addRole("Admin", array("stats.php", "manageusers.php"), array("viewusers.php" => "manageusers.php"));
      

      或来自数据库:

      // conf_* values are set in a config file, or you can pass them in explicitly
      $bouncer->readRolesFromDatabase(conf_hostname, conf_username, conf_password, conf_schema, "mysql");
      

      添加一个用户并赋予他们一些角色(注意:有一个名为 BouncerUser 的类,您的 User 类可以扩展它,它提供了您需要的所有角色功能!):

      $user->addRole("Logged In"); // This Role doesn't exist in the bouncer, but we can set it anyways if we feel like setting another flag on the user's account. This can be useful for displaying content in a page only if a user has a secondary role.
      $user->addRole("Public");
      $user->addRole("Registered User");
      

      然后让保镖管理对您文件的访问:

      $bouncer->manageAccess($user->getRoles(), substr($_SERVER["PHP_SELF"], 1), "fail.php");
      // Any time the user tries to go to a page they don't have access to, they will get to
      // fail.php. Any time they try to go to a page that is overridden for them, they will 
      // get to the overriding page.
      

      如果您只想在用户有权查看的情况下在页面中显示内容,只需将其包装在:

      if($user->hasRole("Registered User")){
          echo "The content";
      }
      

      我认为对于您描述的问题,这将是一个很好的解决方案!

      【讨论】: