我正在努力在CakePHP中实现ACL.在阅读了蛋糕手册中的文档以及其他一些教程,博客文章等之后,我发现Aran Johnson的优秀教程已经帮助填补了许多空白.他的例子似乎与我在一些地方见过的其他人发生冲突 - 特别是在他使用的ARO树结构中.
在他的示例中,他的用户组被设置为级联树,最常用的用户类型位于树的顶部,并且其子节点为每个更受限制的访问类型分支.在其他地方,我经常看到每个用户类型都是相同通用用户类型的子级.
你如何在CakePHP中设置你的ARO和ACO?任何和所有提示赞赏!
CakePHP的内置ACL系统非常强大,但在实际实现细节方面记录不佳.我们在许多基于CakePHP的项目中使用的系统如下所示.
它是对其他地方记录的一些组级访问系统的修改.我们的系统的目标是建立一个简单的系统,用户在组级别上获得授权,但他们可以对由他们创建的项目或每个用户具有特定的附加权限.我们希望避免必须为表中的每个用户(或者更具体地说,针对每个ARO)创建特定条目aros_acos
.
我们有一个Users表和一个Roles表.
用户
user_id, user_name, role_id
角色
id, role_name
为每个角色创建ARO树(我们通常有4个角色 - 未授权访客(id 1),授权用户(id 2),站点主持人(id 3)和Administrator(id 4)):
cake acl create aro / Role.1
cake acl create aro 1 Role.2 ... etc ...
在此之后,您必须使用SQL或phpMyAdmin或类似的方法为所有这些添加别名,因为cake命令行工具不会这样做.我们所有人都使用'Role- {id}'和'User- {id}'.
然后我们创建一个ROOT ACO -
cake acl create aco / 'ROOT'
然后为此ROOT下的所有控制器创建ACO:
cake acl create aco 'ROOT' 'MyController' ... etc ...
到目前为止很正常.我们在aros_acos表中添加了一个额外的字段_editown
,我们可以将其用作ACL组件的actionMap中的附加操作.
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中,我们有以下几点:
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是否可以访问:
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('*'); } } }
将setupAuth()
和checkAccess()
方法调用中AppController
的beforeFilter(
)回调.isMine
AppControler中也有一个方法(见下文),它只检查所请求项的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中编辑的(纯粹为了清晰 - 我们的样板代码中有很多东西在这里毫无意义).我也删除了很多错误检查的东西,但这是它的本质:
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; }