zoukankan      html  css  js  c++  java
  • 使用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

  • 相关阅读:
    Win2008 Server MySql安装包详细安装教程
    ef codefirst VS里修改数据表结构后更新到数据库
    c#扩展方法
    c#异步学习笔记
    winform SerialPort串口通信问题
    委托与事件学习笔记
    泛型的优点
    c#泛型约束 (where T:class)
    .net扩展方法
    mvc5视图view不使用默认母版页
  • 原文地址:https://www.cnblogs.com/hongfei/p/2687118.html
Copyright © 2011-2022 走看看