FleaPHP 开发指南 - 8. 如何使用 RBAC 组件实现访问控制

访问|开发指南|控制

如何使用 RBAC 组件?

请务必更新到 1.0.60.553 版本以上,确保 RBAC 组件与文档中的描述相符合

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 调度器。

<?phprequire('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 文件的内容通常使用下面的格式:

<?phpreturn 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',)

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

<?phpreturn 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 */$rolesManager->create(array('rolename' => 'SYSTEM_ADMIN'));$rolesManager->create(array('rolename' => 'POWER_USER'));$rolesManager->create(array('rolename' => 'MANAGER'));

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

用户信息存储服务

只有角色信息,RBAC 还无法工作。我们还需要用户信息存储服务。这些服务由 FLEA_Com_RBAC_UsersManager 来提供。首先建立存储用户信息的数据表(仍然假定使用 MySQL):

CREATE TABLE `users` (  `user_id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,  `username` VARCHAR( 32 ) NOT NULL ,  `password` VARCHAR( 64 ) NOT NULL ,  `email` VARCHAR( 128 ) NOT NULL ,  `created` INT NULL ,  `updated` INT NULL);

特别注意,存储用户信息的数据表中,至少需要用户名、密码和电子邮件地址三个字段。

然后我们就可以很方便的存储和查询用户信息了:

load_class('FLEA_Com_RBAC_UsersManager');class MyUsersManager extends FLEA_Com_RBAC_UsersManager{    var $tableName = 'users';    var $primaryKey = 'user_id';}$usersManager =& get_singleton('MyUsersManager');/* @var $usersManager MyUsersManager */$usersManager->create(array(    'username' => 'dualface',    'password' => '12345678',    'email' => 'dualface@gmail.com',));

上面的代码会建立一个用户名为 dualface 的用户,而密码明文是 12345678。你也许会奇怪,难道密码不需要加密后存储吗?

实际上,FLEA_Com_RBAC_UsersManager 为你自动完成了该项工作。FLEA_Com_RBAC_UsersManager 在建立一个用户或者更新一个用户时,对于密码字段都是特别处理的。所以程序只需要提供密码明文就行了,而不需要自己加密。不过同时也要注意在更新用户时,不要更新密码字段。而是应该使用 FLEA_Com_RBAC_UsersManager::changePassword() 方法来修改用户账户的密码。

FLEA_Com_RBAC_UsersManager 采用什么加密方法来存储密码,是由 FLEA_Com_RBAC_UsersManager::$encodeMethod 变量决定的。默认为 PWD_CRYPT,即使用 crypt() 函数来加密。可用的加密方式还有 PWD_MD5(存储用 md5() 函数编码后的密码)和 PWD_CLEARTEXT(存储密码明文)。由于不同的加密方式,生成的密码长度都不同,所以建立用户信息数据表时,密码字段的长度为 64,而不是常用的 32。

特别注意,crypt() 函数加密的密码超过了 32 个字符,因此一定要确保用户信息表的密码字段有足够的长度。

如果用户信息表的用户名、密码和电子邮件地址三个字段不是默认的 usernamepasswordemail,那么要分别通过 FLEA_Com_RBAC_UsersManager::$usernameFieldFLEA_Com_RBAC_UsersManager::$passwordFieldFLEA_Com_RBAC_UsersManager::$emailField 变量来指定。

FLEA_Com_RBAC_UsersManager 虽然也是一个表数据入口,但是提供了 findByUserId()findByUsernamefindByEmail 等便利的方法。请参考 API 文档中的 FLEA_Com_RBAC_UsersManager 类,了解所有便利方法。

为用户指定角色

有了用户和角色信息,我们还需要将用户信息和角色信息关联起来。这仍然是通过 FLEA_Com_RBAC_UsersManager 来完成。

首先也是建立一个数据表,用来存储用户和角色之间的关联关系:

CREATE TABLE `roles_users` (  `user_id` INT NOT NULL ,  `role_id` INT NOT NULL ,  PRIMARY KEY ( `user_id` , `role_id` ));

接下来修改我们的 FLEA_Com_RBAC_UsersManager 继承类:

load_class('FLEA_Com_RBAC_UsersManager');class MyUsersManager extends FLEA_Com_RBAC_UsersManager{    var $tableName = 'users';    var $primaryKey = 'user_id';    var $rolesFields = 'roles';    var $manyToMany = array(        'tableClass' => 'MyRolesManager',        'mappingName' => 'roles',        'joinTable' => 'roles_users',    );}

新增加的一个 MANY_TO_MANY 用于将用户和角色关联起来。**注意 MANY_TO_MANY 关联的 mappingName 选项一定要和 $rolesFields 变量的值一样。否则用 FLEA_Com_RBAC_UsersManager::fetchRoles() 无法取得用户的角色信息。

不过只是这样的定义,还不能实际使用。我们看一段实际使用的代码:

<?phprequire('FLEA/FLEA.php');// 假定数据库连接信息保存在 APP/Config/DSN.php 文件中register_app_inf('APP/Config/DSN.php');load_class('FLEA_Com_RBAC_RolesManager');class MyRolesManager extends FLEA_Com_RBAC_RolesManager{    var $tableName = 'roles';    var $primaryKey = 'role_id';}load_class('FLEA_Com_RBAC_UsersManager');class MyUsersManager extends FLEA_Com_RBAC_UsersManager{    var $tableName = 'users';    var $primaryKey = 'user_id';    var $manyToMany = array(        'tableClass' => 'MyRolesManager',        'mappingName' => 'roles',        'joinTable' => 'roles_users',    );}$usersManager =& get_singleton('MyUsersManager');/* @var $usersManager MyUsersManager */// 取出用户$user = $usersManager->findByUsername('dualface');// 清空现有的角色信息$user[$usersManager->rolesField] = array();// 取出 POWER_USER 角色$rolesManager =& get_singleton('MyRolesManager');/* @var rolesManager MyRolesManager */$role = $rolesManager->find(array('rolename' => 'POWER_USER'));// 指定给用户$user[$usersManager->rolesField][] = $role[$rolesManager->primaryKey];// 取出 MANAGER 角色$role = $rolesManager->find(array('rolename' => 'MANAGER'));// 指定给用户$user[$usersManager->rolesField][] = $role[$rolesManager->primaryKey];// 保存修改后的用户信息$usersManager->update($user);// 重新从数据库读取用户信息,确定为用户指定的角色信息已经保存到数据库$user = $usersManager->findByUsername('dualface');dump($user);

运行这段代码,可以看到为用户指定的角色信息,确实保存到数据库了:


通常,我们会在应用程序中提供一个管理界面,用于管理用户信息,并且可以为用户指定角色。这样的管理界面类似下图:


上图中的权限实际上就是可以给用户指定的角色,只不过换了一个称谓而已。

用户登录

由于要使用 RBAC 组件进行访问控制,所以我们的用户登录部分要多写几行代码。常见的登录代码如下:

<?phprequire('FLEA/FLEA.php');// 假定数据库连接信息保存在 APP/Config/DSN.php 文件中register_app_inf('APP/Config/DSN.php');load_class('FLEA_Com_RBAC_RolesManager');class MyRolesManager extends FLEA_Com_RBAC_RolesManager{    var $tableName = 'roles';    var $primaryKey = 'role_id';}load_class('FLEA_Com_RBAC_UsersManager');class MyUsersManager extends FLEA_Com_RBAC_UsersManager{    var $tableName = 'users';    var $primaryKey = 'user_id';    var $manyToMany = array(        'tableClass' => 'MyRolesManager',        'mappingName' => 'roles',        'joinTable' => 'roles_users',    );}/** * 模拟登录 */login('dualface', '12345678');/** * 处理用户登录 */function login($username, $password) {    $usersManager =& get_singleton('MyUsersManager');    /* @var $usersManager MyUsersManager */    // 验证用户名和密码是否正确    $user = $usersManager->findByUsername($username);    if (!$user || !$usersManager->checkPassword($password, $user[$usersManager->passwordField])) {        echo "Username invalid or password mismatch.";        exit;    }    // 获取用户角色信息    $roles = $usersManager->fetchRoles($user);    // 获得 FLEA_Com_RBAC 组件实例    $rbac =& get_singleton('FLEA_Com_RBAC');    /* @var $rbac FLEA_Com_RBAC */    // 为了降低服务器负担,我们只在 session 中存储用户ID和用户名    $sessionUser = array(        'USERID' => $user[$usersManager->primaryKey],        'USERNAME' => $user[$usersManager->usernameField],    );    // 将用户ID、用户名和角色信息保存到 session    $rbac->setUser($sessionUser, $roles);    // 登录成功    echo "Login successed, contents of session:";    dump(

如何使用 RBAC 组件?


请务必更新到 1.0.60.553 版本以上,确保 RBAC 组件与文档中的描述相符合


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



ATM 机的一天


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

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


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



ATM 与 RBAC


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

对于没有磁卡或者输入了错误密码的用户,一律拒绝服务,也就是不允许进行任何其他操作; 如果输入了正确的密码,必须判断用户输入哪一种类型,并提供相应的服务界面; 如果用户尝试访问自己不能使用的服务,那么要明确告诉用户这是不可能的。
这个流程中,一共出现了两种角色:信用卡用户管理卡用户。而那些没有磁卡的用户,都属于没有角色一类。RBAC 要能够工作,至少需要两个数据:角色信息访问控制表


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


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


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

唐雷有一张信用卡,他放入 ATM 机并输入了正确的密码。这时,他被 ATM 机认为具有“信用卡用户”角色。 根据上面的判断结果,ATM 机显示了一个操作界面,上面有查询账户余额和取款两项操作按钮。 唐雷按下了“查询账户余额”按钮,ATM 机的查询账户余额功能被调用。 在查询账户余额功能中,再次检查用户的角色信息,确定他可以使用这个功能。 进行一系列操作,然后将唐雷信用卡账户上的余额数字显示到屏幕上。 唐雷很郁闷他的信用卡又透支了,悻悻然取出卡走人了。这时 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 调度器。

<?phprequire('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 文件的内容通常使用下面的格式:

<?phpreturn 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,再按照下列规则进行验证:

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


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

<?phpreturn 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 */$rolesManager->create(array('rolename' => 'SYSTEM_ADMIN'));$rolesManager->create(array('rolename' => 'POWER_USER'));$rolesManager->create(array('rolename' => 'MANAGER'));


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


用户信息存储服务


只有角色信息,RBAC 还无法工作。我们还需要用户信息存储服务。这些服务由 FLEA_Com_RBAC_UsersManager 来提供。首先建立存储用户信息的数据表(仍然假定使用 MySQL):

CREATE TABLE `users` (  `user_id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,  `username` VARCHAR( 32 ) NOT NULL ,  `password` VARCHAR( 64 ) NOT NULL ,  `email` VARCHAR( 128 ) NOT NULL ,  `created` INT NULL ,  `updated` INT NULL);


特别注意,存储用户信息的数据表中,至少需要用户名、密码和电子邮件地址三个字段。


然后我们就可以很方便的存储和查询用户信息了:

load_class('FLEA_Com_RBAC_UsersManager');class MyUsersManager extends FLEA_Com_RBAC_UsersManager{    var $tableName = 'users';    var $primaryKey = 'user_id';}$usersManager =& get_singleton('MyUsersManager');/* @var $usersManager MyUsersManager */$usersManager->create(array(    'username' => 'dualface',    'password' => '12345678',    'email' => 'dualface@gmail.com',));


上面的代码会建立一个用户名为 dualface 的用户,而密码明文是 12345678。你也许会奇怪,难道密码不需要加密后存储吗?


实际上,FLEA_Com_RBAC_UsersManager 为你自动完成了该项工作。FLEA_Com_RBAC_UsersManager 在建立一个用户或者更新一个用户时,对于密码字段都是特别处理的。所以程序只需要提供密码明文就行了,而不需要自己加密。不过同时也要注意在更新用户时,不要更新密码字段。而是应该使用 FLEA_Com_RBAC_UsersManager::changePassword() 方法来修改用户账户的密码。


FLEA_Com_RBAC_UsersManager 采用什么加密方法来存储密码,是由 FLEA_Com_RBAC_UsersManager::$encodeMethod 变量决定的。默认为 PWD_CRYPT,即使用 crypt() 函数来加密。可用的加密方式还有 PWD_MD5(存储用 md5() 函数编码后的密码)和 PWD_CLEARTEXT(存储密码明文)。由于不同的加密方式,生成的密码长度都不同,所以建立用户信息数据表时,密码字段的长度为 64,而不是常用的 32。


特别注意,crypt() 函数加密的密码超过了 32 个字符,因此一定要确保用户信息表的密码字段有足够的长度。


如果用户信息表的用户名、密码和电子邮件地址三个字段不是默认的 usernamepasswordemail,那么要分别通过 FLEA_Com_RBAC_UsersManager::$usernameFieldFLEA_Com_RBAC_UsersManager::$passwordFieldFLEA_Com_RBAC_UsersManager::$emailField 变量来指定。


FLEA_Com_RBAC_UsersManager 虽然也是一个表数据入口,但是提供了 findByUserId()findByUsernamefindByEmail 等便利的方法。请参考 API 文档中的 FLEA_Com_RBAC_UsersManager 类,了解所有便利方法。


为用户指定角色


有了用户和角色信息,我们还需要将用户信息和角色信息关联起来。这仍然是通过 FLEA_Com_RBAC_UsersManager 来完成。


首先也是建立一个数据表,用来存储用户和角色之间的关联关系:

CREATE TABLE `roles_users` (  `user_id` INT NOT NULL ,  `role_id` INT NOT NULL ,  PRIMARY KEY ( `user_id` , `role_id` ));


接下来修改我们的 FLEA_Com_RBAC_UsersManager 继承类:

load_class('FLEA_Com_RBAC_UsersManager');class MyUsersManager extends FLEA_Com_RBAC_UsersManager{    var $tableName = 'users';    var $primaryKey = 'user_id';    var $rolesFields = 'roles';    var $manyToMany = array(        'tableClass' => 'MyRolesManager',        'mappingName' => 'roles',        'joinTable' => 'roles_users',    );}


新增加的一个 MANY_TO_MANY 用于将用户和角色关联起来。**注意 MANY_TO_MANY 关联的 mappingName 选项一定要和 $rolesFields 变量的值一样。否则用 FLEA_Com_RBAC_UsersManager::fetchRoles() 无法取得用户的角色信息。


不过只是这样的定义,还不能实际使用。我们看一段实际使用的代码:

<?phprequire('FLEA/FLEA.php');// 假定数据库连接信息保存在 APP/Config/DSN.php 文件中register_app_inf('APP/Config/DSN.php');load_class('FLEA_Com_RBAC_RolesManager');class MyRolesManager extends FLEA_Com_RBAC_RolesManager{    var $tableName = 'roles';    var $primaryKey = 'role_id';}load_class('FLEA_Com_RBAC_UsersManager');class MyUsersManager extends FLEA_Com_RBAC_UsersManager{    var $tableName = 'users';    var $primaryKey = 'user_id';    var $manyToMany = array(        'tableClass' => 'MyRolesManager',        'mappingName' => 'roles',        'joinTable' => 'roles_users',    );}$usersManager =& get_singleton('MyUsersManager');/* @var $usersManager MyUsersManager */// 取出用户$user = $usersManager->findByUsername('dualface');// 清空现有的角色信息$user[$usersManager->rolesField] = array();// 取出 POWER_USER 角色$rolesManager =& get_singleton('MyRolesManager');/* @var rolesManager MyRolesManager */$role = $rolesManager->find(array('rolename' => 'POWER_USER'));// 指定给用户$user[$usersManager->rolesField][] = $role[$rolesManager->primaryKey];// 取出 MANAGER 角色$role = $rolesManager->find(array('rolename' => 'MANAGER'));// 指定给用户$user[$usersManager->rolesField][] = $role[$rolesManager->primaryKey];// 保存修改后的用户信息$usersManager->update($user);// 重新从数据库读取用户信息,确定为用户指定的角色信息已经保存到数据库$user = $usersManager->findByUsername('dualface');dump($user);


运行这段代码,可以看到为用户指定的角色信息,确实保存到数据库了:


通常,我们会在应用程序中提供一个管理界面,用于管理用户信息,并且可以为用户指定角色。这样的管理界面类似下图:


上图中的权限实际上就是可以给用户指定的角色,只不过换了一个称谓而已。


用户登录


由于要使用 RBAC 组件进行访问控制,所以我们的用户登录部分要多写几行代码。常见的登录代码如下:

___FCKpd___17


上面的代码中第一次出现了对 FLEA_Com_RBAC 的使用。通常我们只需要用到 FLEA_Com_RBAC::setUser() 方法。这个方法将用户信息和对应的角色信息保存到 session。为了节约服务器资源,我们要尽量减少保存在 session 中的内容。


运行这个脚本,可以看到如下的输出:


大家在开发自己的应用程序时,基本上可以把 login() 函数的内容照搬过去。


用户注销


处理用户注销非常简单,通常用 session_destroy() 销毁 session 数据就可以了。如果只想清除用户登录信息,而不影响 session 中的其他信息,可以用下面两行代码:

$rbac =& get_singleton('FLEA_Com_RBAC');$rbac->clearUser();



实现访问控制


实际上,做完上面几个步骤,我们的 RBAC 已经可以工作了。你可以尝试登录系统,然后访问那些受到保护的控制器。然后再从系统注销后,重新访问受保护的控制器。


目前,FleaPHP 的 RBAC 在处理 ACT 上,还不够灵活。每个控制器的 ACT 都是从文件载入的,而不是从数据库。但有聪明的开发者已经想出了变通的做法。


那就是把从数据库获取控制器 ACT 的代码写在控制器的 .act.php 文件中,例如:


APP\Controller\MyController.act.php

<?php$modelACT =& get_singleton('Model_ControllerACTProvider');/* @var $modelACT Model_ControllerACTProvider */return $modelACT->getACT('MyController');?>


当然,我们还要实现一个 Model_ControllerACTProvider 表数据入口:

<?phpload_class('FLEA_Db_TableDataGateway');class Model_ControllerACTProvider extends FLEA_Db_TableDataGateway{    var $tableName = 'controller_acts';    var $primaryKey = 'controller_name';    function getACT($controllerName) {        $row = parent::find(array($this->primaryKey => strtoupper($controllerName)));        return unserialize($row['act']);    }    function setACT($controllerName, $ACT) {        $row = array(            $this->primaryKey => strtoupper($controllerName),            'act' => serialize($ACT)        );        return parent::create($row);    }}?>


以及一个数据表:

CREATE TABLE `controller_acts` (  `controller_name` VARCHAR( 32 ) NOT NULL ,  `act` TEXT NOT NULL ,  `created` INT NULL ,  `updated` INT NULL ,  PRIMARY KEY ( `controller_name` ))


这样一来,我们就可以在数据库中保存控制器的 ACT 了。

 SESSION);}?>
上面的代码中第一次出现了对 FLEA_Com_RBAC 的使用。通常我们只需要用到 FLEA_Com_RBAC::setUser() 方法。这个方法将用户信息和对应的角色信息保存到 session。为了节约服务器资源,我们要尽量减少保存在 session 中的内容。

运行这个脚本,可以看到如下的输出:


大家在开发自己的应用程序时,基本上可以把 login() 函数的内容照搬过去。

用户注销

处理用户注销非常简单,通常用 session_destroy() 销毁 session 数据就可以了。如果只想清除用户登录信息,而不影响 session 中的其他信息,可以用下面两行代码:

___FCKpd___18


实现访问控制

实际上,做完上面几个步骤,我们的 RBAC 已经可以工作了。你可以尝试登录系统,然后访问那些受到保护的控制器。然后再从系统注销后,重新访问受保护的控制器。

目前,FleaPHP 的 RBAC 在处理 ACT 上,还不够灵活。每个控制器的 ACT 都是从文件载入的,而不是从数据库。但有聪明的开发者已经想出了变通的做法。

那就是把从数据库获取控制器 ACT 的代码写在控制器的 .act.php 文件中,例如:

APP\Controller\MyController.act.php

___FCKpd___19

当然,我们还要实现一个 Model_ControllerACTProvider 表数据入口:

___FCKpd___20

以及一个数据表:

___FCKpd___21

这样一来,我们就可以在数据库中保存控制器的 ACT 了。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索密码
, rbac
, 验证
, 控制器
, 用户
, atm机源程序
, 信息
, 角色
, 角色验证
, yii2_rbac
, yii2_rbac菜单
RBAC类
访问矩阵 rbac、flash组件指南、vue 访问子组件、vue 访问组件方法、数据库访问组件,以便于您获取更多的相关知识。

时间: 2024-10-30 13:15:23

FleaPHP 开发指南 - 8. 如何使用 RBAC 组件实现访问控制的相关文章

FleaPHP 开发指南 - 5. 应用程序设置

程序|开发指南 FleaPHP 是一个具有高度灵活性的框架.开发者可以用不同的设置来调整 FleaPHP 的工作方式.这些设置统一保存在名为"应用程序设置"的数据源中. 实际上,除了 FleaPHP 框架本身,应用程序也可以使用"应用程序设置"来保存应用程序运行时需要的各种设置. FleaPHP 的应用程序设置有一个默认配置文件,保存在 FLEA/Config/Default_APP_INF.php 中.应用程序可以在入口文件(例如 index.php)中修改这些设

FleaPHP 开发指南 - A2. 使用 PATHINFO 和 URL 重写

开发指南 FleaPHP 应用程序通过分析 URL 地址来确定要执行的控制器及动作,以及传递给动作方法的参数. 默认情况下,FleaPHP 应用程序通过 http://www.example.com/index.php?controller=test&action=benchmark&source=1 这样的 URL 地址来访问应用程序的每一个功能.但有时候你希望 URL 地址能够更好看一点,例如上面的地址变为: http://www.example.com/index.php/test/

FleaPHP 开发指南 - 7. 数据表关联

开发指南|数据 数据表关联是指两个或者多个数据表的记录之间的逻辑关系. 例如: 每一个公民都有一个身份证号码 每一位作者都写了多本(0-n)书籍,而每一本书籍都有多个(1-n)作者 每一篇文章都有多个(0-n)评论 每一个评论都属于一篇文章 目前,FleaPHP 支持四种类型的数据表关联,分别是: HAS_ONE: 当前表的每一条记录都拥有最多一条(0–1)关联记录 HAS_MANY: 当前表的每一条记录都拥有多条(0-n)关联记录 MANY_TO_MANY: 当前表的每一条记录都和其他表的多条

FleaPHP 开发指南 - 6. 访问数据库

访问|开发指南|数据|数据库 这一章我们就来看看 FleaPHP 的数据库访问功能.体验一下 FleaPHP 出色的自动化 CRUD 能力. 连接数据库 在 htdocs 目录中创建子目录 testDB,并在子目录下创建文件 test1.php 文件,内容如下: <?phprequire('../FLEA/FLEA.php');__FLEA_PREPARE();// 准备数据库连接信息$dsn = array( 'driver' => 'mysql', 'host' => 'localh

FleaPHP 开发指南 - 4. 命名规范和目录结构

规范|开发指南 FleaPHP 的命名规则和目录结构初看上去比较复杂,但习惯以后,你会发现这种命名规则带来许多好处.因此像 Zend Framework 也是采用同样的做法. 当然,FleaPHP 对于应用程序的命名规则和目录结构没有强制性要求.只不过采用一致的命名规则和目录结构,可以方便对应用程序维护.同时 FleaPHP 应用程序开发者之间也可以更容易的进行协作. 全局函数的命名规则 全局函数的命名规则是用"_"分隔全小写的单词.例如 get_cache().echo_h().同时

FleaPHP 开发指南 - 3. 应用程序入口

程序|开发指南 与许多开发框架一样,FleaPHP 通常也使用一个入口文件来启动框架,并运行应用程序代码.虽然 FleaPHP 并不要求应用程序必须使用单一的入口文件,不过在本系列文章中,我大部分时间都使用单一入口文件. 单一入口应用程序 在 FleaPHP 应用程序使用 MVC 模式时,应用程序的所有功能都通过一个单一的文件来调用.这种通过单一文件来执行所有功能的应用程序,称为单一入口应用程序.有关单一入口应用程序的一些信息,可以参考 ChinaUnix 上的一篇帖子. 许多著名的 PHP 应

VS2003开发指南文档----中文

vs2003|开发指南|中文 下载链接来自微软高级应用开发俱乐部知识库:1.VS 2003 Guide.doc 目录:I. 执行概述 7简介 9设计目标 11Visual Studio .NET 2003 的新增功能 13II. 评估标准 15III. Microsoft 应用程序平台和工具 19可靠而强大的平台 23Windows 服务器系列 23.NET Framework 24Microsoft 企业级服务器 28今天和明天的连接平台 35智能客户端开发 35基于 Web 的开发 37基于

jBPM-4.0中文开发指南-第4章 架构

第 4 章 架构 4.1. APIs 流程虚拟机包含4个集成的API,在不同的执行模式下,覆盖完整的流程工作. 每个API都有特定的目的,满足下面的架构. 开发指南-第4章 架构-jbpm开发入门指南"> 图 4.1. 流程虚拟机中的4个API 服务接口用在应用代码中,与流程虚拟机进行交互,它将运行在支持事务的持久化模式下,后端基于数据库. 这是用户将PVM作为一个工作流引擎使用的最常用的方式. 如果不想使用持久化方式执行流程,可以直接使用客户端API来处理流程和执行对象. 客户端API对

Struts开发指南之MVC架构

模型-视图-控制器(MVC)是80年代Smalltalk-80出现的一种软件设计模式,现在已经被广泛的使用. 1.模型(Model) 模型是应用程序的主体部分.模型表示业务数据,或者业务逻辑. 2.视图(View) 视图是应用程序中用户界面相关的部分,是用户看到并与之交互的界面. 3.控制器(controller) 控制器工作就是根据用户的输入,控制用户界面数据显示和更新model对象状态. 开发指南之MVC架构-springmvc开发指南pdf"> MVC 式的出现不仅实现了功能模块和显