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还有没有其他地方用到)

posted @ 2013-04-09 20:40  Art_Spring  阅读(961)  评论(0)    收藏  举报