使用Zend_Auth和Zend_Acl進行登錄認證及根據用戶角色進行權限控制


  在CRM系統開發中,根據不同的用戶組分配不同的權限是一件再正常不過的事情。ZF框架提供的Zend_Auth和Zend_Acl這兩個組件就是用來幫我們完成類似工作的。

Zend_Auth登錄認證:  

Zend_Auth組件的使用很容易,相信大家看了下面的圖文解說之后就會明白的。

解釋:

Zend_Auth_Adapter_Interface中提供了一個接口,我們需要自己去實現

代碼如下:

<?php
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::FAILURE

Zend_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

<?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 "<script>
                        $.messager.alert('提醒','您沒有操作權限', 'warning');
                    </script>";
                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(自定義)

<?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變量中,這樣以后就不必每次都查詢數據庫去獲取了

至於,如何編輯用戶組的權限,將其保存到數據庫中,可以有多種方法。

我是將權限以json的格式形式存入數據庫中的,而在服務器上創建一個Permission.php文件,以二維數組的形式進行保存,每當需要添加權限時,得手動編輯該文件,而要給不同的用戶組分配不同的權限的時候,采用復選框的方式進行勾選即可。

原創文章:WEB開發

轉載請注明出處:http://www.cnblogs.com/hongfei/archive/2012/09/16/2687118.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM