我正在努力在CakePHP中实现ACL。 阅读Cake手册中的文档以及其他一些教程,博客文章等之后,我发现Aran Johnson的出色教程帮助填补了许多空白。 尽管在某些地方,他的示例似乎与我所看到的其他示例发生了冲突-特别是在他使用的ARO树结构中。
在他的示例中,他的用户组被设置为级联树,最普通的用户类型位于树的顶部,并且其子级针对每种更严格的访问类型进行分支。 在其他地方,我通常将每种用户类型视为相同的通用用户类型的子级。
如何在CakePHP中设置ARO和ACO? 任何和所有提示表示赞赏!
CakePHP的内置ACL系统确实功能强大,但是在实际实现细节方面的文档很少。我们在许多基于CakePHP的项目中成功使用的系统如下。
它是对某些在其他地方记录过的组级访问系统的修改。我们系统的目标是建立一个简单的系统,在该系统中,可以在组级别上授权用户,但是他们可以对他们创建的项或基于每个用户的项具有特定的附加权限。我们希望避免在aros_acos表中为每个用户(或更具体地说,为每个ARO)创建一个特定的条目。
我们有一个用户表和一个角色表。
用户数
user_id, user_name, role_id
的角色
id, role_name
为每个角色创建ARO树(我们通常有4个角色-未经授权的访客(ID 1),授权的用户(ID 2),站点主持人(ID 3)和管理员(ID 4)):
cake acl create aro / Role.1
cake acl create aro 1 Role.2 ... etc ...
此后,您必须使用SQL或phpMyAdmin或类似方法为所有这些方法添加别名,因为Cake命令行工具无法执行此操作。我们对所有人都使用"角色-{id}"和"用户-{id}"。
然后,我们创建一个ROOT ACO-
cake acl create aco / 'ROOT'
然后为该ROOT目录下的所有控制器创建ACO:
cake acl create aco 'ROOT' 'MyController' ... etc ...
到目前为止,一切正常。我们在aros_acos表中添加了一个名为_editown的附加字段,该字段可用作ACL组件的actionMap中的附加动作。
1 2 3 4 5 6 7 8 9 10 11 12
| CREATE TABLE IF NOT EXISTS `aros_acos` (
`id` int(11) NOT NULL auto_increment,
`aro_id` int(11) default NULL,
`aco_id` int(11) default NULL,
`_create` int(11) NOT NULL default '0',
`_read` int(11) NOT NULL default '0',
`_update` int(11) NOT NULL default '0',
`_delete` int(11) NOT NULL default '0',
`_editown` int(11) NOT NULL default '0',
PRIMARY KEY (`id`),
KEY `acl` (`aro_id`,`aco_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8; |
然后,我们可以将Auth组件设置为使用'crud'方法,该方法针对AclComponent :: check()验证请求的控制器/操作。在app_controller中,我们遵循以下原则:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| private function setupAuth() {
if(isset($this->Auth)) {
....
$this->Auth->authorize = 'crud';
$this->Auth->actionMap = array( 'index' => 'read',
'add' => 'create',
'edit' => 'update'
'editMine' => 'editown',
'view' => 'read'
... etc ...
);
... etc ...
}
} |
同样,这是相当标准的CakePHP东西。然后,我们在AppController中有一个checkAccess方法,该方法添加了组级别的内容以检查是否检查组ARO或用户ARO的访问权限:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| private function checkAccess() {
if(!$user = $this->Auth->user()) {
$role_alias = 'Role-1';
$user_alias = null;
} else {
$role_alias = 'Role-' . $user['User']['role_id'];
$user_alias = 'User-' . $user['User']['id'];
}
// do we have an aro for this user?
if($user_alias && ($user_aro = $this->User->Aro->findByAlias($user_alias))) {
$aro_alias = $user_alias;
} else {
$aro_alias = $role_alias;
}
if ('editown' == $this->Auth->actionMap[$this->action]) {
if($this->Acl->check($aro_alias, $this->name, 'editown') and $this->isMine()) {
$this->Auth->allow();
} else {
$this->Auth->authorize = 'controller';
$this->Auth->deny('*');
}
} else {
// check this user-level aro for access
if($this->Acl->check($aro_alias, $this->name, $this->Auth->actionMap[$this->action])) {
$this->Auth->allow();
} else {
$this->Auth->authorize = 'controller';
$this->Auth->deny('*');
}
}
} |
在AppController的beforeFilter()回调中调用setupAuth()和checkAccess()方法。 AppControler中也有一个isMine方法(请参见下文),该方法仅检查所请求项的user_id与当前经过身份验证的用户相同。为了清楚起见,我将其省略。
真的就是全部。然后,您可以允许/拒绝特定的组访问特定的acos-
cake acl grant 'Role-2' 'MyController' 'read'
cake acl grant 'Role-2' 'MyController' 'editown'
cake acl deny 'Role-2' 'MyController' 'update'
cake acl deny 'Role-2' 'MyController' 'delete'
我确定你知道图片。
无论如何,这个答案比我预期的要更长的时间,可能几乎没有任何意义,但我希望它对您有所帮助...
-编辑-
根据要求,这里是一个经过编辑的方法(纯粹是为了清楚起见-我们的样板代码中有很多东西在这里是没有意义的)isMine()方法,我们在AppController中拥有该方法。我也删除了很多错误检查的内容,但这是它的本质:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| function isMine($model=null, $id=null, $usermodel='User', $foreignkey='user_id') {
if(empty($model)) {
// default model is first item in $this->uses array
$model = $this->uses[0];
}
if(empty($id)) {
if(!empty($this->passedArgs['id'])) {
$id = $this->passedArgs['id'];
} elseif(!empty($this->passedArgs[0])) {
$id = $this->passedArgs[0];
}
}
if(is_array($id)) {
foreach($id as $i) {
if(!$this->_isMine($model, $i, $usermodel, $foreignkey)) {
return false;
}
}
return true;
}
return $this->_isMine($model, $id, $usermodel, $foreignkey);
}
function _isMine($model, $id, $usermodel='User', $foreignkey='user_id') {
$user = Configure::read('curr.loggedinuser'); // this is set in the UsersController on successful login
if(isset($this->$model)) {
$model = $this->$model;
} else {
$model = ClassRegistry::init($model);
}
//read model
if(!($record = $model->read(null, $id))) {
return false;
}
//get foreign key
if($usermodel == $model->alias) {
if($record[$model->alias][$model->primaryKey] == $user['User']['id']) {
return true;
}
} elseif($record[$model->alias][$foreignkey] == $user['User']['id']) {
return true;
}
return false;
} |