代码之家  ›  专栏  ›  技术社区  ›  johnny

授权系统设计问题

  •  1
  • johnny  · 技术社区  · 15 年前

    我正试图想出一个做身份验证和授权的好方法。这是我的东西。欢迎发表评论,我希望如此。

    我在Mac服务器上有PHP。 我有Microsoft用户帐户广告。

    当用户登录到Intranet时,我使用LDAP查询广告。

    我的设计问题是关于如何处理这些广告信息。一位同事建议在广告中使用命名约定,以避免使用中间数据库。例如,我有一个网页,personnel_Payroll.php。我得到了URL,并通过URL和广告用户查询了集团人员工资单的广告。如果登录用户在该组中,则他们有权查看该页。我必须为每个页面都有一个组,或者至少为通用身份验证的用户域用户。

    页面上的控件会变得更复杂。例如,假设页面或网格上有一个按钮,只有管理人员才能看到。在我的广告中,我需要一个“个人工资单”按钮作为一个组。如果用户在这个组中,他们会得到这个按钮。如果一个页面具有多个不同级别的授权,我可以拥有多个组。

    是的,我的广告会很大,但是如果我不这样做,其他的东西会,无论是MySQL(或其他数据库),文本文件,httpd.conf等等。

    对于传递URL或控件名称和经过身份验证的用户的各种项目,我将拥有一个通用的php funciton。

    使用这样的安全命名约定,并将AD用作存储库,是否存在本质上的错误?我得留下来。为什么不广告?

    感谢您的评论。

    编辑:您认为这个方案会因为LDAP调用而导致超慢的页面吗?

    编辑:我不是第一个想到这个的人。对此,我们深表感谢。

    编辑:谢谢大家。很抱歉,我不能给你更多的回答分数。我得选一个。

    5 回复  |  直到 15 年前
        1
  •  4
  •   Adam Batkin    15 年前

    我想知道是否有一种不同的表达和存储权限的方式,可以更干净、更高效地工作。

    大多数应用程序被划分为功能区域或角色,并且权限是基于这些[广泛]区域分配的,而不是基于每页权限。例如,您可能拥有如下权限:

    • 使用应用
    • 创建用户
    • 重新设置用户密码
    • 查看工资数据
    • 修改PayRollData

    或者,对于角色,您可以有:

    • 应用用户
    • 应用程序管理
    • 佩洛拉敏

    角色(可能还有每个功能权限)可能已经映射到Active Directory中存储的数据,例如现有的AD组/角色。如果不这样做,那么维护页面权限比维护页面权限要容易得多。可以将权限作为用户组(用户位于组中,因此具有权限或不具有权限)来维护,也可以作为自定义属性来维护:

    dn: cn=John Doe,dc=example,dc=com
    objectClass: top
    objectClass: person
    objectClass: webAppUser
    cn: John Doe
    givenName: John
    ...
    myApplicationPermission: UseApplication
    myApplicationPermission: ViewPayrollData
    

    这样做的好处是模式更改最小。如果您使用组,则AD(以及地球上的其他所有LDAP服务器)已经具有该功能,并且如果您使用这样的自定义属性,则只有一个属性(并且可能是 objectClass , webAppUser 在上面的示例中)需要添加。

    接下来,你需要决定如何 使用 数据。一种可能是在用户登录并将其存储在其会话中的Web服务器端时检查用户的权限(找出他们所在的组或被授予的权限)。存在这样的问题:权限更改只在用户登录时生效,而不是立即生效。如果您不希望权限经常更改(或者当用户同时使用系统时),这可能是一种合理的方法。这有一些变化,例如在经过一定时间后重新加载用户的权限。

    另一种可能,但性能影响更严重(负面)的是根据需要检查权限。在这种情况下,您将更频繁地访问广告服务器,从而导致负载增加(在Web服务器和广告服务器上)、网络流量增加和延迟/请求时间延长。但您可以确保权限始终是最新的。

    如果您仍然认为将单个页面和按钮名称作为权限检查的一部分很有用,那么您可以拥有一个页面/按钮的全局“映射”权限,并通过该权限进行所有权限查找。一些东西(完全未测试,主要是伪代码):

    $permMap = array(
        "personnel_payroll" => "ViewPayroll",
        "personnel_payroll_myButton" => "EditPayroll",
        ...
    );
    
    function check_permission($elementName) {
        $permissionName = $permMap[$elementName];
        return isUserInLdapGroup($user,$permissionName);
    }
    
        2
  •  2
  •   Richard Levasseur    15 年前

    使用广告获得许可的想法没有缺陷,除非你的广告不能扩展。如果使用本地数据库更快/更可靠/更灵活,那么就使用它。

    但是,使用命名约定来查找正确的安全角色是非常脆弱的。您将不可避免地遇到自然映射与真实映射不对应的情况。愚蠢的事情,比如你希望网址是“finbiz”,但它已经在广告中被称为“商业金融”-你是复制组并保持同步,还是在你的应用程序中重新映射…?有时,它就像“finbiz”和“finbiz”一样简单。

    在我看来,最好避免这种问题,例如,使用“185”组而不是“金融业务”或“商业金融”,或其他一些你可以控制的关键。

    不管 怎样 如果您获得了您的权限,最后不得不缓存它,那么您将不得不处理陈旧的缓存数据。

    如果必须使用LDAP,那么创建一个权限ou(或与“schema”等效的ad)会更有意义,这样您就可以将任意实体映射到这些权限。缓存这些数据,您应该确定。

    编辑:

    部分问题似乎是为了避免中间数据库——为什么不让中间数据库成为主要数据库呢?定期(通过钩子或轮询)将本地权限数据库同步到AD,可以避免两个重要问题:1)脆弱的命名约定;2)外部数据源故障。

        3
  •  1
  •   Alex    15 年前

    这样的页面速度会非常慢(在我看来,每次用户导航以了解他可以做什么时,您都会重新查询AD LDAP),除非您实现某种缓存,但随后可能会遇到不稳定的权限问题(在您不知道的情况下,对AD撤消/添加了权限)。

    我将保留权限和这些单独的权限,而不使用ad作为存储库来管理您的应用程序特定授权。相反,使用一个单独的存储提供程序,这将更容易维护和必要时扩展。

        4
  •  1
  •   Marsh Ray    15 年前

    为安全起见使用命名约定是否存在本质上的错误,例如 这和使用广告作为存储库?我得留下来。为什么不广告?

    从逻辑上讲,在LDAP/AD中使用组进行授权正是它设计的目的。特定用户的LDAP查询应该相当快。

    实际上,AD对于在服务器之间复制数据更改所需的时间是非常不可预测的。如果有一天你的应用程序最终出现在一个大森林里,域控制器分布在整个大陆,你会后悔把细粒度的数据放在那里。说真的,要为我合作过的一些客户复制这些东西需要一个小时。当服务器重新启动后,事情神奇地开始工作时,就会出现一些神秘的情况。

    可以为“myapp用户”、“经理”、“工资单”类型的组使用目录。尽量避免重复和浪费的LDAP查找。

    如果您在Windows上,一种可能是在本地磁盘上为每个授权项目创建一个小文件。这将为您提供“安全对象”。然后,您的应用程序可以模拟用户并尝试打开该文件。这利用了微软多年来在优化这方面的巨大投资。也许你可以在Mac上做这个。

    另外,请查看Apache的mod_auth_ldap。据说它支持“通过使用LDAP过滤器表示策略可以实现复杂的授权策略”。

    我不知道你的应用做什么,它不使用某种数据库的东西。这对你来说是个好消息,因为你没有走捷径!这里是一个包含JSON的纯文本文件可以走很长一段路的地方。

        5
  •  0
  •   MidnightLightning    15 年前

    您所描述的似乎是一个伴随身份验证的访问控制列表(ACL),因为您将超越“组”到该组中的特定操作。要创建一个没有独立于您的身份验证方法的数据库的ACL,我建议您查看Zend PHP框架,特别是 ACL module .

    在您的广告设置中,将用户分配到组(您提到“经理”,可能会有“用户”、“管理员”,可能是某些部门特定的组,如果用户不是组的一部分,则可能有一个通用的“公共”)。Zend ACL模块允许您定义“资源”(与示例中的页面名称相关)以及这些资源中的“操作”。然后在会话中将它们保存为一个对象,可以引用该对象来确定用户是否具有访问权限。例如:

    <?php
    $acl = new Zend_Acl();
    $acl->addRole(new Zend_Acl_Role('public'));
    $acl->addRole(new Zend_Acl_Role('users'));
    $acl->addRole(new Zend_Acl_Role('manager'), 'users');
    
    $acl->add(new Zend_Acl_Resource('about')); // Public 'about us' page
    $acl->add(new Zend_Acl_Resource('personnel_payroll')); // Payroll page from original post
    
    $acl->allow('users', null, 'view'); // 'users' can view all pages
    $acl->allow('public', 'about', 'view'); // 'public' can only view about page
    $acl->allow('managers', 'personnel_payroll', 'edit'); // 'managers' can edit the personnel_payroll page, and can view it since they inherit permissions of the 'users' group
    
    // Query the ACL:
    echo $acl->isAllowed('public', 'personnel_payroll', 'view') ? "allowed" : "denied"; // denied
    echo $acl->isAllowed('users', 'personnel_payroll', 'view') ? "allowed" : "denied"; // allowed
    echo $acl->isAllowed('users', 'personnel_payroll', 'edit') ? "allowed" : "denied"; // denied
    echo $acl->isAllowed('managers', 'personnel_payroll', 'edit') ? "allowed" : "denied"; // allowed
    ?>
    

    将acl与ad分开的好处是,随着代码的更改(以及在不同区域内可能发生的“操作”),授予对它们的访问权限的位置是相同的,而不是必须管理ad服务器才能进行更改。而且,您使用的是一个现有的(稳定的)框架,因此您不必使用自己的isauthorized函数来重新设计轮子。