大家写的关于cake1.2 Auth+ACL都过于简略,在百度空间上搜到一个比较详细的,在这 里 。
但是在这位老兄给出的Auth的例子中,并没有关于ACL的东西,而是基于controller,经过昨天一天的奋战,终于将ACL搞定,赶快写下总结。
一、适用范围
不是所有网站都需要引入ACL这么强劲的权限控制,它的粒度太细了,可以精确到每个人访问每个action的权限,所以大多数情况下基于 controller的Auth组件已经足够了。我这里总结适用的情况有:
a 权限需要分级也就是权限继承
比如,有集团账户,然后集团账户可以自己开公司账户,还可以开员工账户,很明显员工是有一部分集团权限的,但又不可能完全一致,这时候ACL就提供了合理 的处理机制。
b 权限需要可编辑,可修改
想要随时控制客户权限,做到随时针对组或者个人来修改的可配置权限控制也只能使用ACL了。
二、加载时机
ACL中,请求发起者,也就是aros,可以动态加载,因为用户数据是要进数据库的,是能够动态控制和驱动的。但是acos也就是我们的每个action 呢?不可能程序员每写一段代码就人工修改一次aco表吧?也不可能每写完一个action就为这个函数分配一次权限吧?所以最好的添加Auth的顺序为:
a 添加Auth组件,但设置$this->Auth->allowedAction(array('*'))以保证开发工作的顺利进行;
b 完成User,Group的Model,Controller,views逻辑 ;
c 添加login逻辑,只测试能否正确登录即可;
d 完成其他所有业务逻辑,抛开权限控制;
e 最后再加入ACL控制,完整权限控制;
三、Auth组件(除ACl之外配置)
具体参看我另 外一篇文章
唯一要注意的是,添加Auth组件之后,添加User时会自动加密,加密方式和判断密码时所用一样,调用的 Auth->hashPassowords($this->data),执行在beforeFilter之后,action之前。所以不需要 单独为post过来的$this->data['User']['Password']加密。
四、Auth中的'crud'和'actions'的不同
在所有业务逻辑基本完成之后,把ACL加上,别忘了在合适的位置设 置$this->Auth->authorize = 'actions';或$this->Auth->authorize = 'crud‘;
下面详细介绍这2种调用方式的不同:
'crud'([create|read|update|delete])是一种按操作类型来控制权限的方式,它控制用户对一类操作的权限。
比如一个用户可以编辑自己发表的文章,还可以编辑自己发表的评论,在ArticlesController中,2个action可能分别叫 articleEdit和feadbackEdit,你嫌分别给每个action权限太麻烦了,可以近似的认为2个操作属于一种类型,即update,或 者你可以理解为拥有编辑自己文章权限的人一定拥有编辑自己评论的权限。
这样你就要把Auth的类型设为crud,但你要告诉Cake这2个权限是同属于'update'的,即定义actionMap,类似下面这样:
$this->Auth->actionMap = array(
'articleEdit' => 'update',
'feadbackEdit' => 'update'
);
在规定权限的使用这样写:
$this->Acl->allow($aro, 'controllers/Articles/','update');
如果你观察过aros_acos这样表你就会马上明白cake是怎么根据操作类型进行判断的。
'action'相对就简单的多了,与actionMap无关,只是要规定权限时不同,要加上具体的action:
$this->Acl->allow($aro, 'controllers/Articles/articleEdit');
五、初始化ACL以及自动控制ARO的超酷行为(behavior)
这里官网的说 明 和实 例 更有效果,但如果你英文不太行,看我的也可以。
初始话ACL很简单,你可以选择手动添加[app/config/sql]目录下的db_acl.sql文件,或者使用Bake(官方说 明 )。
ACL本身就像一个裁判,或者说判断机制,判断什么样的球员(aros)可以拿什么样的球(acos)。 这 里有一个很好的,完全自动的添加aro的方法,在web中发起请求的当然是用户,所以只需要在添加、修改、删除的时候顺便把aro建好就可以了。幸运的 是,cake想到了这点,有一个behavior叫acl,你只需在Model中添加如下代码就能实现自动控制:
var $actsAs = array('Acl' => array('requester'));
function parentNode() {
if (!$this->id && empty($this->data)) {
return null;
}
$data = $this->data;
if (empty($this->data)) {
$data = $this->read();
}
if (!$data['User']['group_id']) {
return null;
}else {
return array('Group' => array('id' => $data['User']['group_id']));
}
}
function afterSave($created) {
if (!$created) {
$parent = $this->parentNode();
$parent = $this->node($parent);
$node = $this->node();
$aro = $node[0];
$aro['Aro']['parent_id'] = $parent[0]['Aro']['id'];
$this->Aro->save($aro);
}
}
解释: parentNode是为了规定他的父节点,afterSave是判断如果更新了,定义新的父节点。赶快试一下 吧,像魔法一样的behavior。研读一下core/libs/models/behaviors/acl.php你会收获更多~
六、添加ACOS
球员们有了,球呢?前面叙述过了,要在所有逻辑完成后,为actions添加到aco列表。这里也有一个函数可以实现这个功能,你只要随便找个地方运行它 就可以了。
/**
* Rebuild the Acl based on the current controllers in the application
*
* @return void
*/
function buildAcl() {
$log = array();
$aco =& $this->Acl->Aco;
$root = $aco->node('controllers');
if (!$root) {
$aco->create(array('parent_id' => null, 'model' => null, 'alias' => 'controllers'));
$root = $aco->save();
$root['Aco']['id'] = $aco->id;
$log[] = 'Created Aco node for controllers';
} else {
$root = $root[0];
}
App::import('Core', 'File');
$Controllers = Configure::listObjects('controller');
$appIndex = array_search('App', $Controllers);
if ($appIndex !== false ) {
unset($Controllers[$appIndex]);
}
$baseMethods = get_class_methods('Controller');
$baseMethods[] = 'buildAcl';
// look at each controller in app/controllers
foreach ($Controllers as $ctrlName) {
App::import('Controller', $ctrlName);
$ctrlclass = $ctrlName . 'Controller';
$methods = get_class_methods($ctrlclass);
// find / make controller node
$controllerNode = $aco->node('controllers/'.$ctrlName);
if (!$controllerNode) {
$aco->create(array('parent_id' => $root['Aco']['id'], 'model' => null, 'alias' => $ctrlName));
$controllerNode = $aco->save();
$controllerNode['Aco']['id'] = $aco->id;
$log[] = 'Created Aco node for '.$ctrlName;
} else {
$controllerNode = $controllerNode[0];
}
//clean the methods. to remove those in Controller and private actions.
foreach ($methods as $k => $method) {
if (strpos($method, '_', 0) === 0) {
unset($methods[$k]);
continue;
}
if (in_array($method, $baseMethods)) {
unset($methods[$k]);
continue;
}
$methodNode = $aco->node('controllers/'.$ctrlName.'/'.$method);
if (!$methodNode) {
$aco->create(array('parent_id' => $controllerNode['Aco']['id'], 'model' => null, 'alias' => $method));
$methodNode = $aco->save();
$log[] = 'Created Aco node for '. $method;
}
}
}
debug($log);
$this->autoRender = false;
}
但是它不可能去判断你删了那些action或者改过哪些名字,所以当你有改动的时候最好清空acos表,然后重新执行它,要记住产品模式下,这个函数是不 应该出现的。
七、实现权限控制
现在该写裁判规则了,这个当然要程序员欲定义,要编辑权限的话那是另外一个令人兴奋的话题。我们先手动添加,同样的找个地方运行它然后在上线之后删掉它, 这里我用的actions的方式:
function initDB() {
$group =& $this->User->Group;
//Allow admins to everything
$group->id = 1;
$this->Acl->allow($group, 'controllers');
//allow managers to posts and widgets
$group->id = 2;
$this->Acl->deny($group, 'controllers');
$this->Acl->allow($group, 'controllers/Events/');
$this->Acl->allow($group, 'controllers/Users/index');
$this->Acl->allow($group, 'controllers/Users/view');
$this->Acl->allow($group, 'controllers/Users/add');
$this->Acl->allow($group, 'controllers/Users/edit');
$this->autoRender = false;
$aro_acos = $this->User->query("SELECT * FROM aros_acos");
debug($aro_acos);
}
同样的你可以去读Acl组件(core/libs/controllers/components/acl.php)吸取能量。
总结:ACL相对复杂,但控制的更灵活。只是每个页面会多出6-7条sql语句,分别是遍历每个表结构,和acl查询语句。如果有什么好的建议希望你们可 以和我交流。