在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