细粒度访问控制

4
我熟悉多种用于Web管理应用程序的用户身份验证方式,甚至还了解各种保持授权状态的技术...
然而,我的问题是,您如何推荐我实现一个细粒度的访问控制机制,提供以下功能:
1. 用户属于“角色”或“组”,例如“销售员”,“计划”等。 2. 管理员菜单系统仅显示与用户角色相关的“页面”功能。 3. 这些页面中的特定功能具有约束条件-例如,在“新预订”页面上,“销售员”用户只能“在未来”发出预订,并且在“编辑预订”页面上可以“从现在起一周内”编辑预订。但是,“计划”用户可能被允许“向前一个星期”进行回顾性预订,并且可以编辑自己进行的“任何时间段”的预订,但只能编辑其他人进行的预订,“直到明天”...
我知道我可以实现基本的基于角色的系统来满足第1点...我觉得我应该将整个应用程序拆分成代码块,每个块都有自己的对象ID-权限ID关系,以便我可以扫描权限数据库以查看哪些对象可用-这将帮助我完成第2点。
任何想法如何构建表单控件,例如,仅为“销售”用户显示将来的日期(但为计划用户显示最多“一周前”的日期),然后将其与POST解析器中的一行配对,以检查日期是否实际上在预期范围内?我已经尝试过保存每个代码块到数据库中的想法,然后有一个对象表根据权限表动态构建代码,这样服务器上唯一的“文件”就是db连接文件!欢迎任何想法...(即使您的背景不是php / MySQL)

以下是Zed Shaw在CUSEC演讲中探讨“ACL已死”的更多见解 - http://vimeo.com/2723800

4个回答

4

警告,以下内容涉及大量Zend框架!

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

对于第3个问题,您需要在模型中查询ACL对象并手动执行许多操作。您还可以利用Zend Framework来处理表单,并根据用户角色权限包含特定的表单元素验证器。

编辑:

如果您不想使用ZF路线,那么至少可以查看如何在ZF中处理ACL。


嗨Goran,感谢您提出Zend ACL的建议 - 我已经浏览了很多Zend文档!但是,由于我们需要实现自己的'本地'方法,我尝试找到了一些将Zend ACL集成到其他应用程序中的示例,以便我们可以借鉴一些想法...请参见下面的另一个想法! - boatingcow

2
如果您想建立真正细粒度的访问控制(FGAC),请查看我关于MySQL这个主题的文章:
MySQL 5.0细粒度访问控制(FGAC)
基本上,您不希望业务代码依赖于FGAC实现,也不希望将FGAC代码混合在业务规则的select语句的where子句中。这篇文章展示了避免SQL语句混乱的解决方案。

1
为了实现一种“本地”方法,而不是依赖框架,我一直在尝试以下方法。有人能评估这种方法吗?您预见到任何问题吗?
// 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组件,只是增加了权限规范,并且合并了操作和资源。这样,您将无法向资源添加继承,但似乎您不需要它,而且目前我认为资源继承是一个坏主意。权限似乎很有趣,但是静态的。即它们与特定规则相关联。 - koen
有一件我不喜欢的事情是 $this_user == $author_user。如果我理解你的例子正确的话,这实际上是一个硬编码的规则:只有当用户是作者时才允许访问。类似这样的检查应该放在访问控制列表(acl)中。 - koen
谢谢Koen。据我所知,我仍然可以添加继承(因为这是我想要实现的一个功能),但关键问题是您提到的“硬编码”规则-通常,我不介意将用户流程控制粘贴在代码中(因为它不严格属于授权; 它更像是身份验证...),但我很想知道您是否有任何想法,如何将这样的规则分离成ACL?感谢您的建议! - boatingcow
我不喜欢接受自己的答案,但对于任何正在寻找的人,这就是我们正在使用的! - boatingcow

0
我开发了一个名为PHP-Bouncer的库,我认为它非常适合您的需求。它目前支持完全管理的访问,这将允许您在每个页面上使用单个调用(当然我建议使用include),并自动重定向人们如果他们没有访问页面的权限,以及从数据库自动检索角色(如果您使用包含的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管理您的文件访问:

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

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接