返回首页
当前位置: 主页 > 网络编程 > Php实例教程 >

fleaPHP里的RBAC权限模型

时间:2015-03-02 20:26来源:知行网www.zhixing123.cn 编辑:麦田守望者

RBAC 是英文(Role-Based Access Control)的缩写,也就是基于角色的访问控制。RBAC 的定义比较晦涩,我就以比较生动的形式来阐述什么是 RBAC。


ATM 机的一天

假设有一台 ATM(自动提款机)放在街边,我们来看看这个 ATM 度过的一天。

  1. 早上,有一个家伙走到 ATM 面前,对着机器说:“芝麻开门,芝麻开门,给我 100 块!”。很显然 ATM不会有任何动作。失望之余,这个家伙踢了 ATM 一脚走了。
  2. 中午,一位漂亮的 Office lady 走到 ATM 机面前,放入她的信用卡,输入密码后,取出了 1200 块钱。当然,这些钱很快就会变成一件衣服或是化妆品。
  3. 下班时分,银行的工作人员来到 ATM 机器面前,放入一张特制的磁卡,然后输入密码。从中查询到 ATM 机器内还有充足的现金,无需补充。所以他很高兴的开着车去下一台 ATM 机器所在地了。

现在我们要开发一台具有同样功能的 ATM 机,应该怎么做呢?

首先,我们的 ATM 机不能让人随便取钱,不然银行会破产的。接下来,ATM 机需要一个让人们放入磁卡并输入密码的设备。人们放入磁卡并输入密码后,ATM 机还要能够判断这张磁卡的卡号和密码是否有效,并且匹配。之后,ATM 机必须判断磁卡的卡号属于哪种类型,如果是信用卡,那么则显示查询账户余额和取款的界面。如果是特制的磁卡,则显示 ATM 机内的现金余额。


ATM 与 RBAC

上面的例子显得有点荒诞,但是却是一个典型的基于角色的访问控制。

  1. 对于没有磁卡或者输入了错误密码的用户,一律拒绝服务,也就是不允许进行任何其他操作;
  2. 如果输入了正确的密码,必须判断用户输入哪一种类型,并提供相应的服务界面;
  3. 如果用户尝试访问自己不能使用的服务,那么要明确告诉用户这是不可能的。

这个流程中,一共出现了两种角色:信用卡用户和管理卡用户。而那些没有磁卡的用户,都属于没有角色一类。RBAC 要能够工作,至少需要两个数据:角色信息和访问控制表。

角色信息通常是指某个用户具有的角色,例如你持有一张信用卡,那么你就具有“信用卡用户”这个角色。如果你持有一张管理卡,那么你就具有“管理卡用户”这个角色。如果你既没有信用卡,又没有管理卡,那么你就没有上述两种角色。

有了角色信息,RBAC 系统还需要一个访问控制表。访问控制表(Access Control Table)是一组数据,用于指出哪些角色可以使用哪个功能,哪些角色不能使用哪个功能。例如在 ATM 机中,具有“信用卡用户”角色,就可以使用查询账户余额和取款两项功能;而具有“管理卡用户”角色,就可以使用查询 ATM 机内现金余额的动能。

我们来模拟一次 ATM 机的操作:

  1. 唐雷有一张信用卡,他放入 ATM 机并输入了正确的密码。这时,他被 ATM 机认为具有“信用卡用户”角色。
  2. 根据上面的判断结果,ATM 机显示了一个操作界面,上面有查询账户余额和取款两项操作按钮。
  3. 唐雷按下了“查询账户余额”按钮,ATM 机的查询账户余额功能被调用。
  4. 在查询账户余额功能中,再次检查用户的角色信息,确定他可以使用这个功能。
  5. 进行一系列操作,然后将唐雷信用卡账户上的余额数字显示到屏幕上。
  6. 唐雷很郁闷他的信用卡又透支了,悻悻然取出卡走人了。这时 ATM 自动清除当前的角色信息,为下一次操作做好准备。

从上面可以看出,RBAC 充当了系统的一道安全屏障。所有的操作都需要进过 RBAC 验证过后才能使用。这样充分保证了系统的安全性。


RBAC 概念

在 FleaPHP 的 RBAC 组件中,只有下列几项概念需要理解:

  • 用户:应用程序的使用者;
  • 角色:一个名字,可以为用户指定多个角色(0-n);
  • 访问控制表(ACT):一个数组,用来指明哪些功能可以被哪些角色访问或者限制访问。

除了上述三个概念,要想 RBAC 系统能够正常工作,还需要用户信息管理器、角色信息管理器和访问控制器三个部件。

  • 用户信息管理器:提供用户信息的存储、查询服务,以及为用户指定角色信息;
  • 角色信息管理器:提供角色信息的存储和查询服务
  • 访问控制器:根据角色信息和访问控制表进行验证

FleaPHP 中已经实现了上述三个部件,所以开发者要做的功能就比较简单了。


使用 RBAC

FleaPHP 中提供了 FLEA_Com_RBAC、FLEA_Com_RBAC_UsersManager 和 FLEA_Com_RBAC_RolesManager 三个部件,以及 FLEA_Dispatcher_Auth 调度器。

其中,FLEA_Com_RBAC_UsersManager 提供用户信息存储服务,而 FLEA_Com_RBAC_RolesManager 提供角色信息存储服务。FLEA_Com_RBAC 则和 FLEA_Dispatcher_Auth 结合,一起提供了访问控制能力。

下面我们来看看 RBAC 到底怎么工作的。

修改应用程序设置

要使用访问控制功能,首先需要修改应用程序设置。让应用程序使用 FLEA_Dispatcher_Auth 调度器,而不是默认的 FLEA_Dispatcher_Simple 调度器。

<?php

require('FLEA/FLEA.php');
set_app_inf('dispatcher', 'FLEA_Dispatcher_Auth');
/**
 * ...
 * 其他初始化代码
 * ...
 */

run();

?>

FLEA_Dispatcher_Auth 调度器和 FLEA_Dispatcher_Simple 调度器的基本功能一样。但在调用控制器动作方法前,FLEA_Dispatcher_Auth 调度器会通过 FLEA_Com_RBAC 组件获取保存在 session 中的用户角色信息,然后再读取控制器的访问控制表(ACT)。最后调用 FLEA_Com_RBAC::check() 方法检查用户拥有的角色是否可以访问这个控制器及要调用的控制器动作。

验证通过,则控制器动作方法会被调用,否则将显示错误信息,或者调用应用程序设置dispatcherAuthFailedCallback 指定的错误处理程序。

准备控制器的 ACT 文件

设置好应用程序后,接下来要做的就是为控制器准备 ACT 文件。

ACT 文件和控制器文件同名,并且保存在同一个目录下,只是扩展名为 .act.php。例如控制器Controller_Default 的文件名是 Controller/Default.php,那么该控制器的 ACT 文件名就是 Controller/Default.act.php。

ACT 文件的内容通常使用下面的格式:

<?php

return array(
    'allow' => 'POWER_USER, SYSTEM_ADMIN',

    'actions' => array(
        'remove' => array(
            'allow' => 'SYSTEM_ADMIN',
        ),

        'create' => array(
            'deny' => 'SYSTEM_ADMIN',
        ),
    ),
);

?>

可以看到,ACT 文件只是单纯的返回一个数组。这个数组遵循下面的格式:

array(
    'allow' => '允许访问该控制器的角色名',
    'deny' => '禁止访问该控制器的角色名',

    'actions' => array(
        '动作名' => array(
            'allow' => '允许访问该动作的角色名',
            'deny' => '禁止访问该动作的角色名',
        ),
        // .... 更多动作
    ),
);

在上面的格式中,角色名可以是多个,例如“POWER_USER, MANAGER”。只需要用“,”分隔多个角色名就可以了。

通常,我们只需要为控制器指定 allow 或者 deny 就可以了。但有时候我们要允许多个角色都可以访问该控制器,但该控制器中的特定方法只允许上述角色中部分角色可以访问。这时,我们可以通过'动作名' => array('allow' => '角色名', 'deny' => '角色名') 的方式来指定该控制器动作特有的 ACT。

为了便于开发,FleaPHP 预定义了几个角色,分别是:

  • RBAC_EVERYONE:表示任何用户(不管该用户是否具有角色信息)
  • RBAC_HAS_ROLE:表示具有任何角色的用户(该用户必须有角色信息)
  • RBAC_NO_ROLE:表示不具有任何角色的用户
  • RBAC_NULL:表示该设置没有值

 特别注意,上述四个预定义角色并不是字符串,而是常量。因此必须以 'allow' => RBAC_EVERYONE 这样方式使用。并且不能和其他角色混用,例如 'allow' => RBAC_EVERYONE . ', POWER_USER' 就是错误的。

验证规则

在验证时,首先从 session 中取出用户的角色信息。取出来的角色信息是一个数组,数组中每一个项为用户具有的一个角色。例如:

$userRoles = array(
    'POWER_USER',
    'MANAGER',
);

然后取出控制器的 ACT,再按照下列规则进行验证:

  1. 如果 ACT 的 allow 为 RBAC_EVERYONE,则进行下列检查:
    1. 如果 ACT 的 deny 为 RBAC_NULL,则表示表示允许任何角色访问,验证结果为 true;
    2. 如果 ACT 的 deny 为 RBAC_NO_ROLE,则表示用户只要具有角色信息,就可以访问。因此如果用户的角色信息为空白,则验证结果为 false,否则验证结果为true;
    3. 如果 ACT 的 deny 为 RBAC_HAS_ROLE,则表示用户只要具有角色信息,就不允许访问。因此如果用户的角色信息为空白,则验证结果为 true,否则验证结果为false;
    4. 如果 ACT 的 deny 为 RBAC_EVERYONE,则表示这个 ACT 存在冲突(因为 allow 和 deny 都为 RBAC_EVERYONE);
    5. 检查用户的角色名是否出现在 deny 指定的角色名中,如果有,则验证结果为 false,否则验证结果为 true。
  1. 如果 ACT 的 allow 为 RBAC_HAS_ROLE,则表示用户只要具有角色信息,就可以访问。因此如果用户的角色信息为空白,则验证结果为 false,否则验证结果为 true;
  1. 如果 ACT 的 allow 为 RBAC_NO_ROLE,则表示用户只要具有角色信息,就不允许访问。因此如果用户的角色信息为空白,则验证结果为 true,否则验证结果为false;
  1. 如果 ACT 的 allow 为 RBAC_NULL,则进行下列检查:
    1. 如果 ACT 的 deny 为 RBAC_NULL,则表示 ACT 既没有设置允许访问的角色,也没有设置拒绝访问的角色,这时候假定为允许访问,所以验证结果为 true;
    2. 如果 ACT 的 deny 为 RBAC_NO_ROLE,则表示用户只要具有角色信息,就可以访问。因此如果用户的角色信息为空白,则验证结果为 false,否则验证结果为true;
    3. 如果 ACT 的 deny 为 RBAC_HAS_ROLE,则表示用户只要具有角色信息,就不允许访问。因此如果用户的角色信息为空白,则验证结果为 true,否则验证结果为false;
    4. 如果 ACT 的 deny 为 RBAC_EVERYONE,则表示拒绝任何角色访问,验证结果为 false;
    5. 5) 检查用户用户的角色名是否出现在 deny 指定的角色名中,如果有,则验证结果为 false,否则验证结果为 true。
  1. 验证进行到这里时,ACT 的 allow 必然是角色名,因此只要用户具有的角色名在 allow 指定的角色名中,验证结果就为true,否则验证结果为false。
ACT 示例

之所有进行这么复杂的验证,是考虑到多种多样的验证需求。看看下面几个例子:

  • 只要具有角色,就允许访问:
array(
    'allow' => RBAC_HAS_ROLE
);
  • 只要具有角色,就不允许访问:
array(
    'deny' => RBAC_HAS_ROLE
);
  • 用户具有角色,并且没有 POWER_USER 角色时,允许访问:
array(
    'allow' => RBAC_HAS_ROLE,
    'deny' => 'POWER_USER'
)
  • 用户只要没有 MANAGER 角色,就允许访问:
array(
    'deny' => 'MANAGER',
)
  • 用户具有 POWER_USER 和 MANAGER 角色时允许访问,但具有 SYSTEM_ADMIN 角色时拒绝访问。对于这个 ACT 定义,如果用户的角色为 ‚POWER_USER, GUEST‘,则允许访问。如果用户的角色为 ‚POWER_USER, SYSTEM_ADMIN‘,则不允许访问。因为 deny 的优先级总是大于 allow。
array(
    'allow' => 'POWER_USER, MANAGER',
    'deny' => 'SYSTEM_ADMIN',
)

上面虽然只说了对控制器的验证,但对控制器动作的验证规则是完全相同的。只是只有当用户被允许访问控制器时,才会对要访问的控制器动作进行验证。这种机制提供非常高的灵活性,例如:

<?php

return array(
    'allow' => 'POWER_USER, SYSTEM_ADMIN',

    'actions' => array(
        'remove' => array(
            'allow' => 'SYSTEM_ADMIN',
        ),

        'create' => array(
            'deny' => 'SYSTEM_ADMIN',
        ),
    ),
);

?>

用户只要具有 POWER_USER 和 SYSTEM_ADMIN 两个角色之一,就可以访问这个控制器。但只有当用户具有 SYSTEM_ADMIN 角色时,才允许使用控制器的 remove 动作。反之,如果用户具有 SYSTEM_ADMIN 角色,就不允许使用控制器的 create 动作。

 特别注意,不管是 allow 还是 deny,只要用户具有的角色有其中之一符合条件就会判定该规则有效。例如 'allow' => 'POWER_USER, SYSTEM_ADMIN' 只要用户具有 POWER_USER 和 SYSTEM_ADMIN 两个角色之一,就算作允许访问。而不需要用户同时具有 POWER_USER 和 SYSTEM_ADMIN 角色。

角色信息存储服务

准备好控制器的 ACT 后,我们还需要使用角色信息存储服务,来管理应用程序中会用到的角色信息。

首先,我们要建立如下的数据表(假定使用 MySQL)。这个数据表很简单,每行记录存储一个角色。

CREATE TABLE `roles` (
  `role_id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
  `rolename` VARCHAR( 32 ) NOT NULL ,
  `created` INT NULL ,
  `updated` INT NULL
);

然后我们在应用程序中就可以从 FLEA_Com_RBAC_RolesManager 派生一个对象来管理角色信息了:

load_class('FLEA_Com_RBAC_RolesManager');
class MyRolesManager extends FLEA_Com_RBAC_RolesManager
{
    var $tableName = 'roles';
    var $primaryKey = 'role_id';
}

$rolesManager =& get_singleton('MyRolesManager');
/* @var $rolesManager MyRolesManager */
$role = array('rolename' => 'SYSTEM_ADMIN');
$rolesManager->create($role);
$role = array('rolename' => 'POWER_USER');
$rolesManager->create($role);
$role = array('rolename' => 'MANAGER');
$rolesManager->create($role);

事实上,FLEA_Com_RBAC_RolesManager 是一个表数据入口对象,所以可以直接使用 create()、find() 等方法来添加、查询角色信息。

顶一下
(0)
0%
踩一下
(0)
0%
------分隔线----------------------------
标签(Tag):php php教程 php实例教程 php5 php源代码 php基础教程 php技巧 php6
------分隔线----------------------------
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
验证码:点击我更换图片
猜你感兴趣