Yii 中使用 SRBAC 扩展 实现 RBAC 无法验证用户是否拥有该权限
最近正在研究 PHP 的 Yii 框架的权限模块,自带的 RBAC 组件是非常强大的。但是缺乏管理功能,自己写一个权限管理模块也着实麻 烦。 在百度上google一番,发现 yii 官方网站的扩展模块竟然有一个 SRBAC 模块来实现 RBAC 的管理。down 下来学习使用 之。。
此扩展的配置方法网上也比比皆是,本文不是讲述如何配置所以就不详细介绍了,可以参考(http://www.cnblogs.com/mrcoke/articles/2407821.html)。
是 SRBAC 确实给力只需几个步骤就能实现权限管理模块。兴匆匆的配置好后添加了一些 roles、tasks、operations,并将他们配置给已存在用户。在准备给 view 中进行权限判断时 :
Yii::app()->user->checkAccess('admin')
确发现它的返回值竟然是 false !! 检查多遍确定 authitem 表中确实有 admin 这一项,且在 authassignment 表中 admin 也与当前用户有关联关系,怎么会验证失败!!
没办法,只好看 yii 源码解决问题。
Yii::app()->user 获得到的是一个CWebUser实例, CWebUser 中 checkAccess() 源码是:
{yii_path}/framework/web/auth/CWebUser.php
public function checkAccess($operation,$params=array(),$allowCaching=true) { if($allowCaching && $params===array() && isset($this->_access[$operation])) return $this->_access[$operation]; $access=Yii::app()->getAuthManager()->checkAccess($operation,$this->getId(),$params); if($allowCaching && $params===array()) $this->_access[$operation]=$access; return $access; }
关键之处:
$access=Yii::app()->getAuthManager()->checkAccess($operation,$this->getId(),$params);
第一个参数 $operation 自然就是所需检测的权限项(AuthItem)第二个参数 $this->getId() 就是对应的 authassignment 表中的 userid 字段了。
由于 SRBAC 中的配置 'userid' 项我设置的是用户表的主键 userid 所以 authassignment 表中的 userid 字段存的也是 userid 而不是 username 了。
回到源代码, $this->getId() 这里的 id 是什么值呢? 且来看看 $this->setId() 是赋的什么值:
protected function changeIdentity($id,$name,$states) { Yii::app()->getSession()->regenerateID(true); $this->setId($id); $this->setName($name); $this->loadIdentityStates($states); }
这里还是看不到 $id 是什么值,继续探索。。。
public function login($identity,$duration=0) { $id=$identity->getId(); $states=$identity->getPersistentStates(); if($this->beforeLogin($id,$states,false)) { $this->changeIdentity($id,$identity->getName(),$states); if($duration>0) { if($this->allowAutoLogin) $this->saveToCookie($duration); else throw new CException(Yii::t('yii','{class}.allowAutoLogin must be set true in order to use cookie-based authentication.', array('{class}'=>get_class($this)))); } $this->afterLogin(false); } return !$this->getIsGuest(); }
终于发现了,$id=$identity->getId(); 真相就在眼前,看看 login 的调用便知晓 id 为何值。
yii 自动生成的 LoginForm.php 中:
public function login() { if($this->_identity===null) { $this->_identity=new UserIdentity($this->username,$this->password); $this->_identity->authenticate(); } if($this->_identity->errorCode===UserIdentity::ERROR_NONE) { $duration=$this->rememberMe ? 3600*24*30 : 0; // 30 days Yii::app()->user->login($this->_identity,$duration); return true; } else return false; }
$identity 实际上就是一个 UserIdentity 实例,而 UserIdentity 继承与 CUserIdentity,
在 CUserIdentity.php 文件中最后一段代码为。
public function getId() { return $this->username; } public function getName() { return $this->username; }
原来不管是 id 还是 name 得到的结果都是 username!! 症结就在此,用 username 和 userid 比对怎么可能成功呢?
最后解决方案:在 UserIdentity 类里面重写 getId 方法:
class UserIdentity extends CUserIdentity { public $userId; public function authenticate() { $username = strtolower($this->username); //将用户输入的用户名变为小写(防止因大小写重名) $user = User::model()->find('LOWER(username)=?', array($username)); if (!isset($user)) $this->errorCode = self::ERROR_USERNAME_INVALID; elseif ($user->password !== Util::encypt($this->password)) $this->errorCode = self::ERROR_PASSWORD_INVALID; else { $this->userId = $user->id; $this->errorCode = self::ERROR_NONE; } return !$this->errorCode; } public function getId() { return $this->userId; } }
问题终于得以解决。
本文讲述主要是体现一个思路和解决问题的过程,语言口语化,且仅为个人不成熟观点,如有不同意见请多多指教。
最后还有一种不是办法的办法(我没有试过..) 在配置 SRBAC 时直接将 userid 的值设为 username...但是不建议这么做,既然有这么一个设置项,它的存在自有它的道理。(没有深究userid还有没有其他地方用到)

浙公网安备 33010602011771号