一聚教程网:一个值得你收藏的教程网站

热门教程

php中Zend_Auth和Zend_Acl进行登录认证及根据用户角色进行权限控制

时间:2022-06-24 18:11:33 编辑:袖梨 来源:一聚教程网

Zend_Auth_Adapter_Interface中提供了一个接口,我们需要自己去实现

代码如下:

 代码如下 复制代码

require_once 'Zend/Auth/Adapter/Interface.php';
class Auth implements Zend_Auth_Adapter_Interface{
    private $_useraccount;
    private $_password;
    private $_db;
    /**
     * 构造函数 设置用户名和密码 数据连接对象  
     *
     * @return void     
     */
    public function __construct($useraccount,$password,$db){  
        $this->_useraccount = $useraccount;
        $this->_password      = $password;
        $this->_db             = $db;
    }
   
    /**    
     * 进行认证 
     * @throws Zend_Auth_Adapter_Exception   
     * @return Zend_Auth_Result
     *
     */
    public function authenticate()
    {
        //默认情况下是认证失败
        $authResult = array(
            'code'     => Zend_Auth_Result::FAILURE,//详参:Zend_Auth_Result
            'identity' => '',
            'info' => array()
           );
          
           //获得登录用户信息
        $result = $this->_getAccountData();
               
        if(isset($result)&&!empty($result)) {//认证成功,则将用户信息存储到session中
            $authResult = array(
                'code'     => Zend_Auth_Result::SUCCESS,
                'identity' => $result['name'],
                'info'     => array('status'=>$result['status'])
            );
            //角色存储  个人缓存空间
            $namespace = Zend_Auth::getInstance()//单例模式
                                 -> getStorage()
                                 -> getNamespace();
            $_SESSION[$namespace]['role']     = $result['group_id'];//所属用户组
            $_SESSION[$namespace]['userInfo'] = $result;
            $_SESSION[$namespace]['userInfo']['lastLoginTime'] = $result['login_time'];
            $_SESSION[$namespace]['userInfo']['lastLoginIp']   = $result['login_ip'];          
//            $_SESSION[$namespace]['userInfo']['password']   = $result['password'];//密码是很重要的,不要写到session中
        }
       
        return new Zend_Auth_Result($authResult['code'], $authResult['identity'], $authResult['info']);
    }
    /**
     * 用户密码加密
     * @param $pwd  原始密码
     * @return string  加密后的密码字符串
     *
     */
    static public function encryptionType($pwd=null) {
        $pwd = md5($pwd);
        return $pwd;
    }
    /**
     * 获得用户数据结构
     *
     * @todo 整理密码的公共类
     */
    private function _getAccountData(){
        $resArr = array();
        $sysUserObj = Base_Dao_Factory::getObject('Admin_Models_User');
        //先登录普通会员帐号
        $data = array(
            'login_name' => $this->_useraccount,
            'login_pwd'  => $this->encryptionType($this->_password)
        );
        $result = $sysUserObj->login($data);
        //判断是否有数据,是则赋值
        if ($result) {
            if (!empty($result[0])) {
                $resArr = $result[0];
            }
        }
        return $resArr;
    }
}

 

解释:在authenticate方法的实现代码中,return一个Zend_Auth_Result对象实例,而查看Zend_Auth_Result的源代码,知道实例化的时候需要传入三个参数:

 @param int $code            身份认证的结果(如:Zend_Auth_Result::SUCCESS)

 @param mixed $identity   用于身份认证的标示符(如:登录名(张三))

 @param array $messages 认证失败的原因数组

而一旦认证成功,则将信息存储到session变量中。

 

 

以上的校验工作都是在同一个action中完成的,这里,我使用的是Zend_Auth类中的getCode方法

getCode方法:返回一个zend_auth_resulet常量标识符用来决定认证失败的类型或者是否认证成功(详参:Zend_Auth_Result)


找不到身份表示的错误:Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND

无效认证导致的错误:  Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID

认证成功:           Zend_Auth_Result::SUCCESS

一般错误:           Zend_Auth_Result::FAILUREZend_Auth中还有其他几个方法:

getStorage方法:

getIdentity():返回认证尝试的身份

isvalid()           返回true表示一个成功的认证尝试
getcode()        返回一个zend_auth_resulet常量标识符用来决定认证失败的类型或者是否认证成功
getIdentity()    返回认证尝试的身份
getmessages() 返回关于认证尝试失败的数组

 代码如下 复制代码

 $auth=Zend_Auth::getInstance();       
 if ($auth->hasIdentity()){
       echo $auth->getIdentity();    //输出:张三
       $auth->clearIdentity();       //清除身份
 }
 echo $auth->getIdentity();          //NULL
 var_dump($auth->getStorage());     //Zend_Auth_Storage_Session对象  Zend_Acl进行权限控制

 实现目标:当发送一个请求(eg:/index.php/shopping/showshop/)的时候,首先应该先进行判断,看是否具有访问权限

用图表示,大概就如下所示:

在action被Zend_Controller_Dispatcher派发之前,会先调用Zend_Controller_Plugin_Abstract类中的preDispatch()方法,因此我们可以继承Zend_Controller_Plugin_Abstract类,在子类中的preDispatch方法中进行权限判断。

代码如下:

 代码如下 复制代码

Verify.php


require_once 'Zend/Controller/Plugin/Abstract.php';
class Verify extends Zend_Controller_Plugin_Abstract
{
    /**
     * 访问控制列表对象
     * @var object
     */
    protected $_acl;
    /**
     * 登录的用户名
     * @var string
     */
    protected $_loginname;
   
    /**
     * 构造函数
     * 初始化访问控制列表
     * @param Acl $acl
     * @todo 未登录的时候,$this -> _loginname是为空的
     */
    public function __construct($acl)
    {
        $this -> _acl = $acl;
        require_once('Zend/Auth.php');
        $this -> _loginname = Zend_Auth::getInstance()->getIdentity();//eg:张三
    }
   
    /**
     * 重写父类的preDispatch方法
     *
     * @param Zend_Controller_Request_Abstract $request
     */
    public function preDispatch(Zend_Controller_Request_Abstract $request)
    {
        //请求信息
        $module     = $request -> module;                //模块
        $controller = $request -> controller;            //请求的控制器
        $action     = $request -> action;                //请求的action
       
        $resource = ucfirst(strtolower($controller));    //资源:一个限制访问的对象
        $action   = strtolower($action);               
        $role     = $this->_acl->getRole();                //角色:一个可以发出请求去访问Resource的对象
       
        //判断是否拥有资源
        if(!($this -> _acl -> has($resource))) {
            $resource = null;
        }

//        $this->_acl->removeAllow($role,$resource);        //可以针对某个role移除权限
        //判断当前用户是有权限执行某个请求
        if(!($this -> _acl -> isAllowed($role, $resource, $action))) {
            if (!$this -> _loginname) {//未登陆的情况
                $module     = 'admin';
                $controller = 'login';
                $action     = 'view';
            }else {                      //没有权限的情况
                echo "";
                exit();
            }
        }
//        else {    //认证成功
//           
//        }
        $request -> setModuleName($module);
        $request -> setControllerName($controller);
        $request -> setActionName($action);
    }
}

解释:思路其实很简单,首先获取当前用户的角色(或超管或访客),然后使用isAllowed方法来判断请求者在整个 web 应用里是否拥有执行功能的许可,从而执行不同的流程控制。

但是,这里涉及到几个问题:

①preDispatch方法在哪里调用呢

②Verify类在哪里进行实例化

③访问控制列表(acl)如何定义

解答:

第一个问题:preDispatch在Zend_Controller_Action的dispatch方法中被调用(502行左右),该方法先于postDispatch被调用。

第二个问题:在入口文件index.php中进行实例化

$acl = new Acl();//自定义的Acl类
$fc = Zend_Controller_Front::getInstance();//取得Zend_Controller_Front类实例
$fc -> registerPlugin(new Verify($acl));以上代码片段的$acl = new Acl()也正是第三个问题将要回答的

第三个问题:关于访问控制列表的定义,及如何添加角色,添加资源,可以仔细看看官方手册,讲得很详细

http://framework.zend.com/manual/1.12/zh/zend.acl.introduction.html

 
Zend_Acl中的几个方法:

allow:增加一条“允许”规则到acl列表

如:$acl->allow('guest', null, 'view');//允许游客具有访问的权限

deny:增加一条“禁止”规则到acl列表

如:$acl->deny('guest', null, 'view');//禁止游客具有访问的权限

isAllowed:判断某个角色(role)是否有权访问某个资源(resource)

remove:删除某个资源及其子资源

removeAll:从ACL中删除所有的资源

removeAllow:从ACL中删除某个role有权访问的资源

removeDeny:从ACL中删除某个role禁止访问的资源

removeRole:从ACL中删除某个角色role

addRole:添加一个唯一的角色到ACL注册表中

hasRole:判断某个角色role是否已注册过

getRole:返回当前用户角色

getRoles:返回一个注册角色的数组

getResources:返回注册过的资源数组

在项目中,我们可以写一个Acl类,继承自Zend_Acl,而角色,资源等的添加定义都可以在自定义类中去实现

好比如这样:

Acl.php(自定义)

 

 代码如下 复制代码
require_once('Zend/Acl.php');
/**
 * 角色权限控制
 *
 */
class Acl extends Zend_Acl
{
    public function __construct()
    {
        $role = $this -> getRole();                    //获取用户角色
        if ($role=='guest') {                        //访客角色
            $roleGuest=new Zend_Acl_Role('guest');    //创建角色guest
            $this->addRole($roleGuest);                //将角色添加到role注册表
            $this -> add(new Zend_Acl_Resource('Login'));
            $this -> add(new Zend_Acl_Resource('User'));
            $this -> allow('guest', null, array('login'));    //允许访客到登录界面
            $this -> allow('guest', null, Array('showuserbydep'));//允许guest根据部门获取用户
        }else {                                    //登录用户的权限
            //如果该角色不存在,则添加到Role注册表中,否则后面的代码会报错
            //eg:Fatal error: Uncaught exception 'Zend_Acl_Role_Registry_Exception' with message 'Role 'admin' not found'
            if (!$this->hasRole($role)) {
                $this -> addRole(new Zend_Acl_Role($role));
            }
                       
            //登录的用户都有的权限
            $this -> add(new Zend_Acl_Resource('Index'));
            $this -> add(new Zend_Acl_Resource('Dep'));
            $this -> add(new Zend_Acl_Resource('Login'));
            $this -> add(new Zend_Acl_Resource('Order'));
            $this -> add(new Zend_Acl_Resource('Shopping'));
            $this -> add(new Zend_Acl_Resource('User'));
            $this -> add(new Zend_Acl_Resource('Authgroup'));
           
            //第三个参数不写,默认具有访问整个控制器的权限
            $this -> allow($role,'Index',Array('index'));
            $this -> allow($role,'Login',Array('index','login','admin','top','left','view','welcome','logout'));
            $this -> allow($role,'Order',Array('orderhistory'));
            $this -> allow($role,'Shopping',Array('showmenu','uploadimage','showshop','showfood'));
            $this -> allow($role,'User','index');
            $this -> allow($role,'Dep',Array('showdep'));
            $this -> allow($role,'Authgroup','index');
           
            $rolePurview = $this -> getRolePurview($role);//角色的权限
            //判断权限数据格式是否正确
            if(!is_Array($rolePurview)){
                echo '权限数据格式有错误!';exit();
            }else{
                foreach ($rolePurview as $controller => $actionArray){
                    //controller资源
                    $resource = ucfirst($controller);
                                   
                    //判断是否拥有资源
                    if(!$this -> has($resource)){//没有资源
                        $this -> add(new Zend_Acl_Resource($resource));//增加资源
                    }
                   
                    //判断资源数据格式是否正确
                    if(!is_Array($actionArray) || empty($actionArray)){
                        echo '资源数据格式有错误!';exit();
                    }else{
                        foreach ($actionArray as $action){
                            $this -> allow($role, $resource, $action);//允许角色访问资源
                        }
                    }
                }
            }
        }
    }
   
    /**
     * 获取用户的角色名
     * @return string
     */
    public function getRole()
    {
        $ns = Zend_Auth::getInstance() -> getStorage() -> getNamespace();
       
        //有session信息
        if(isset($_SESSION[$ns])) {//有登录
            if(isset($_SESSION[$ns]['role'])){//判断有没有登录用户的角色信息
                $role = $_SESSION[$ns]['role'];
            }else {
                $role = 'guest';
            }
        }else {
            $role = 'guest';
        }
        return $role;
    }
   
    /**
     * 获得登录用户的数据数据
     *
     */
    public function getUserInfo()
    {
        $namespace = Zend_Auth::getInstance() -> getStorage() -> getNamespace();
        //用户认证失败则返回false
        if(isset($_SESSION[$namespace]['userInfo'])) {
            return $_SESSION[$namespace]['userInfo'];
        }else{
            return false;
        }
    }
    /**
     * 获得权限数据
     * @return Array   成功返回
     * @return Array   失败返回
     * @todo 只有成功登陆页面的情况下才有数据
     */
    public function getUserPermission()
    {
        $namespace = Zend_Auth::getInstance() -> getStorage() -> getNamespace();
        if(isset($_SESSION[$namespace]['resourcesPurview'])){
            return $_SESSION[$namespace]['resourcesPurview'];
        }else{
            return false;
        }
    }   
    /**
     * 根据角色获取权限(获取指定角色的访问控制列表)
     *
     * @param Array $role
     * @return unknown
     */
    public function getRolePurview($role)
    {
        if(!is_string($role)){
            return false;
        }else {
            $rolePurview = $this -> getAllPurview();
            return $rolePurview[$role];
        }
    }   
    /**
     * 获取所有角色的权限(访问控制列表)
     *
     * @return Array
     */
    public function getAllPurview()
    {
        $rolePurview = Array();
        //判断缓存是否存在,有则从缓存中取,否则从数据库中取
        if (!empty($_SESSION['allRolePurviews'])) {
            $rolePurview = $_SESSION['allRolePurviews'];
        }else{
            $roleDao = Base_Dao_Factory::getObject('Admin_Models_Authgroup');
            $result  = $roleDao -> getAllGroup(Array('id', 'group_purview'));
                       
            if(!empty($result)){
                foreach ($result as $key => $value){
                    $purview[$value['id']] = Zend_Json::decode($value['group_purview']);
                }
            }
            $rolePurview = $purview;
            $_SESSION['allRolePurviews'] = $purview;
        }
        return $rolePurview;
    }   
}

解释:自定义的Acl类中,我们首先要判断当前登录用户是访客还是系统用户,从而针对不同的用户角色给予不同的权限。

通过allow()我们可以很容易的针对不同的角色给予不同的权限控制。

注:对于出“访客”外的其他所有用户角色,都有一些默认的权限

现在我抛出一个问题:系统用户肯定有多个分组(比如:超级管理员和普通用户的权限定然不一样)那如何针对不同的用户组分配不同的权限呢?

这个容易,可以将不同组的权限存到数据库中,要用到的时候,根据不同的用户角色查得其所有的访问权限,在循环遍历下,通过allow将角色权限添加到acl中即可,也就是以上的代码片段:

 

 代码如下 复制代码

//判断资源数据格式是否正确
if(!is_Array($actionArray) || empty($actionArray)){
    echo '资源数据格式有错误!';exit();
}else{
    foreach ($actionArray as $action){
        $this -> allow($role, $resource, $action);//允许角色访问资源
    }
}

比如:


Array
(
    [admin] => Array
        (
            [Login] => Array
                (
                    [0] => index
                    [1] => admin
                    [2] => top
                    [3] => left
                    [4] => view
                    [5] => welcome
                )

            [Authgroup] => Array
                (
                    [0] => index
                    [1] => getpermission
                    [2] => edit
                )

            [Order] => Array
                (
                    [0] => orderhistory
                    [1] => allorders
                    [2] => delorder
                    [3] => latestorders
                    [4] => nopayorders
                    [5] => changepaystate
                    [6] => ordersbyrestaurant
                )

            [Shopping] => Array
                (
                    [0] => showmenu
                    [1] => uploadimage
                    [2] => showshop
                    [3] => showfood
                    [4] => addshop
                    [5] => delshop
                    [6] => editshop
                    [7] => updateshop
                    [8] => changestate
                    [9] => addfood
                    [10] => editfood
                    [11] => delfood
                    [12] => updatefood
                )

            [User] => Array
                (
                    [0] =>
                )

            [System] => Array
                (
                    [0] =>
                )

            [Dep] => Array
                (
                    [0] => showdep
                    [1] => adddep
                    [2] => deldep
                    [3] => viewusersbydep
                )

        )

)

以上是“超级管理员”所拥有的权限

注意一点:在用户登录成功后,可以讲当前用户角色所拥有的权限信息存储到session变量中,这样以后就不必每次都查询数据库去获取了

至于,如何编辑用户组的权限,将其保存到数据库中,可以有多种方法。

热门栏目