在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():返回認證嘗試的身份
$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
