php ci框架使用qeephp框架的acl实现权限验证
1、在libraries文件夹下添加Acl.php类库
1 <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); 2 /** 3 * ACL 实现了权限检查服务 4 * 5 * “基于角色”通过比对拥有的角色和访问需要的角色来决定是否通过权限检查。 6 * 7 * 在进行权限检查时,要求分别提供角色组和访问控制列表(ACL)。 8 * 然后由 QACL 比对角色组和 ACL,并返回检查结果。 9 * 10 * rolesBasedCheck() 用于比对权限,并返回结果。 11 * role_normalize() 方法用于将 roles 角色配置 转换为符合规范的 数组形式。 12 * acl_normalize() 方法用于将 ACL 转换为符合规范的 ACL。 13 */ 14 class Acl 15 { 16 /** 17 * 预定义角色常量 18 */ 19 const ACL_EVERYONE = 'acl_everyone'; 20 const ACL_NULL = 'acl_null'; 21 const ACL_NO_ROLE = 'acl_no_role'; 22 const ACL_HAS_ROLE = 'acl_has_role'; 23 const ALL_CONTROLLERS = 'all_controllers'; 24 const ALL_ACTIONS = 'all_actions'; 25 26 var $uri = array('space'=>'','controller'=>'','action'=>''); 27 var $acl; 28 var $acl_controller; 29 var $acl_action; 30 var $roles; 31 32 function __construct(){ 33 if(function_exists('get_instance') && class_exists('CI_Controller')){ 34 $CI =& get_instance(); 35 $this->uri = array( 36 'space' => $CI->router->fetch_directory(), 37 'controller' => $CI->router->fetch_class(), 38 'action' => $CI->router->fetch_method() 39 ); 40 } 41 } 42 43 /** 44 * 对 roles 进行权限验证 45 * @param array $roles 46 * @param array $acl_uri space空间命名(CI中的controllers/下dir分目录) controller控制器文件 action为控制器处理函数 47 * @param array $acl 自定义权限验证配置 48 * @return array 49 */ 50 function checkAcl($roles, $uri = array(),$acl = array()){ 51 if(!empty($uri)) $this->uri = array_merge($this->uri,$uri); 52 $acl_file = 'acl'; 53 if(isset($this->uri['space'])){ 54 if(!empty($this->uri['space'])){ 55 $acl_file .= '_'.$this->uri['space']; 56 } 57 } 58 if(empty($acl)){ 59 $filepath = APPPATH.'config/'.$acl_file.'.php'; 60 if (!file_exists($filepath)) return TRUE; 61 } 62 $this->roles = $this->roles_normalize($roles); 63 include($filepath); 64 $this->acl = $acl; 65 $this->acl_controller = $this->_controllerACL($this->uri['controller']); 66 return $this->_actionACL($this->uri['action']); 67 } 68 69 /** 70 * 对 roles 整理,返回整理结果 71 * @param array $roles 72 * @return array 73 */ 74 function roles_normalize($roles){ 75 if (!is_array($roles)){ $roles = explode(',', $roles); } 76 return array_map('strtolower',array_filter(array_map('trim',$roles),'strlen')); 77 } 78 79 /** 80 * 对 ACL 整理,返回整理结果 81 * @param array $acl 要整理的 ACL 82 * @return array 83 */ 84 function acl_normalize(array $acl){ 85 $acl = array_change_key_case($acl, CASE_LOWER); 86 $ret = array(); 87 $keys = array('allow', 'deny'); 88 foreach ($keys as $key){ 89 do{ 90 if (!isset($acl[$key])){ $values = self::ACL_NULL; break; } 91 $acl[$key] = strtolower($acl[$key]); 92 if($acl[$key] == self::ACL_EVERYONE || $acl[$key] == self::ACL_HAS_ROLE 93 || $acl[$key] == self::ACL_NO_ROLE || $acl[$key] == self::ACL_NULL){ 94 $values = $acl[$key]; break; 95 } 96 $values = $this->roles_normalize($acl[$key]); 97 if (empty($values)){ $values = self::ACL_NULL; } 98 }while (FALSE); 99 $ret[$key] = $values; 100 } 101 return $ret; 102 } 103 104 /** 105 * 对 controller 访问控制做处理 106 * @param string $controller 107 * @return array 108 */ 109 protected function _controllerACL($controller){ 110 if(isset($this->acl[$controller])){ 111 $this->acl = array_change_key_case($this->acl, CASE_LOWER); 112 return (array)$this->acl[$controller]; 113 } 114 return isset($this->acl[self::ALL_CONTROLLERS]) ? (array)$this->acl[self::ALL_CONTROLLERS] 115 : array('allow' => self::ACL_EVERYONE); 116 } 117 118 /** 119 * 对 action 访问控制做处理 120 * @param string $action 121 * @return array 122 */ 123 protected function _actionACL($action){ 124 if(isset($this->acl_controller['actions'][$action])){ 125 return $this->_rolesBasedCheck($this->acl_controller['actions'][$action]); 126 } 127 if(isset($this->acl_controller['actions'][self::ALL_ACTIONS])){ 128 return $this->_rolesBasedCheck($this->acl_controller['actions'][self::ALL_ACTIONS]); 129 } 130 if(isset($this->acl_controller)){ 131 return $this->_rolesBasedCheck($this->acl_controller); 132 } 133 } 134 135 /** 136 * 进行实际权限校验 137 * @param string $acl 138 * @return array 139 */ 140 protected function _rolesBasedCheck($acl){ 141 $this->acl_action = $this->acl_normalize($acl); 142 if ($this->acl_action['allow'] == self::ACL_EVERYONE){ 143 // 如果 allow 允许所有角色,deny 没有设置,则检查通过 144 if ($this->acl_action['deny'] == self::ACL_NULL){ return TRUE; } 145 146 // 如果 deny 为 acl_no_role,则只要用户具有角色就检查通过 147 if ($this->acl_action['deny'] == self::ACL_NO_ROLE){ 148 if (empty($this->roles)){ return FALSE; } 149 return TRUE; 150 } 151 152 // 如果 deny 为 acl_has_role,则只有用户没有角色信息时才检查通过 153 if ($this->acl_action['deny'] == self::ACL_HAS_ROLE){ 154 if (empty($this->roles)){ return TRUE; } 155 return FALSE; 156 } 157 158 // 如果 deny 也为 acl_everyone,则表示 acl 出现了冲突 159 if ($this->acl_action['deny'] == self::ACL_EVERYONE){ 160 return FALSE; 161 } 162 163 // 只有 deny 中没有用户的角色信息,则检查通过 164 foreach ($this->roles as $role){ 165 if (in_array($role, $this->acl_action['deny'])){ return FALSE; } 166 } 167 return TRUE; 168 } 169 170 do{ 171 // 如果 allow 要求用户具有角色,但用户没有角色时直接不通过检查 172 if ($this->acl_action['allow'] == self::ACL_HAS_ROLE){ 173 if (!empty($this->roles)){ break; } 174 return FALSE; 175 } 176 177 // 如果 allow 要求用户没有角色,但用户有角色时直接不通过检查 178 if ($this->acl_action['allow'] == self::ACL_NO_ROLE){ 179 if (empty($this->roles)){ break; } 180 return FALSE; 181 } 182 183 if ($this->acl_action['allow'] != self::ACL_NULL){ 184 // 如果 allow 要求用户具有特定角色,则进行检查 185 $passed = FALSE; 186 foreach ($this->roles as $role){ 187 if (in_array($role, $this->acl_action['allow'])){ 188 $passed = TRUE; 189 break; 190 } 191 } 192 if (!$passed){ return FALSE; } 193 } 194 } while (FALSE); 195 196 // 如果 deny 没有设置,则检查通过 197 if ($this->acl_action['deny'] == self::ACL_NULL){ return TRUE; } 198 199 // 如果 deny 为 acl_no_role,则只要用户具有角色就检查通过 200 if ($this->acl_action['deny'] == self::ACL_NO_ROLE){ 201 if (empty($this->roles)){ return FALSE; } 202 return TRUE; 203 } 204 // 如果 deny 为 acl_has_role,则只有用户没有角色信息时才检查通过 205 if ($this->acl_action['deny'] == self::ACL_HAS_ROLE){ 206 if (empty($this->roles)){ return TRUE; } 207 return FALSE; 208 } 209 210 // 如果 deny 为 acl_everyone,则检查失败 211 if ($this->acl_action['deny'] == self::ACL_EVERYONE){ return FALSE; } 212 213 // 只有 deny 中没有用户的角色信息,则检查通过 214 foreach ($this->roles as $role){ 215 if (in_array($role, $this->acl_action['deny'])){ return FALSE; } 216 } 217 return TRUE; 218 } 219 } 220 // END Controller class 221 222 /* End of file Acl.php */ 223 /* Location: ./application/libraries/Acl.php */
2、在项目文件夹下的core文件夹中创建核心类MY_Controller.php,在该文件中添加权限验证,所有需要验证的控制器文件都要继承该类。
1 <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); 2 3 class Auth_Controller extends CI_Controller { 4 5 var $role = ''; 6 var $uid = 0; 7 var $username = ''; 8 9 var $_ckey=''; 10 var $_akey=''; 11 var $_mkey=''; 12 13 function __construct() 14 { 15 parent::__construct(); 16 //引入Acl类库 17 $this->load->library(array('Acl','common')); 18 19 //假设现在角色为admin,这里可以自已定义获取角色权限的方法。 20 $this->role = 'gm'; 21 //执行权限判断 22 if(!$this->acl->checkAcl( $this->role )){ 23 if( method_exists($this,'_on_access_denied') ) $this->_on_access_denied(); 24 } 25 26 27 } 28 29 /* 30 * @name _on_access_denied 访问无权限时处理方法 31 * @return null 32 */ 33 protected function _on_access_denied() 34 { 35 header('Content-type: text/html; charset=utf-8'); 36 echo '无权限';exit; 37 // header('Location: '.config_item('sso_admin_url').'?ref='.urlencode(config_item('ref_url')));exit; 38 } 39 40 protected function _db_error(){ 41 echo 'DB_error();';exit; 42 } 43 44 protected function _redirectMessage($heading,$message,$url,$time=5,$hidden_script='') 45 { 46 $this->load->view('common/show_msg',array('message_caption'=>$heading,'message_body'=>$message,'redirect_url'=>$url,'redirect_delay'=>$time,'hidden_script'=>$hidden_script)); 47 return; 48 } 49 50 protected function _FailMessage($info,$msg,$url = array()){ 51 if(is_array($url)) $url = $this->common->Get_Url($url); 52 return $this->_redirectMessage($this->common->Get_ErrorMsg($info),$this->common->Get_ErrorMsg($msg),$url); 53 } 54 } 55 // END Controller class 56 57 /* End of file Auth_Controller.php */ 58 /* Location: ./application/libraries/Auth_Controller.php */
3、权限配置文件,在项目的config文件夹下添加acl.php文件,格式参考下面的示例代码。
该文件中配置了testacl控制器的访问权限为ACL_HAS_ROLE,即所有拥有角色的用户都有权限访问。
actions中配置的是testacl控制器中index方法和add方法的访问权限。“allow”表示可以访问的角色,“deny”表示不允许访问的角色。
1 <?php 2 if ( ! defined('BASEPATH')) exit('No direct script access allowed'); 3 //遵循qeephp中的acl规则 4 $acl['all_controllers'] = array( 5 'allow'=>'ACL_HAS_ROLE',//表示所有拥有角色的用户 6 ); 7 $acl['testacl'] = array( 8 'allow'=>'ACL_HAS_ROLE', 9 'actions'=>array( 10 'index'=>array('allow' => 'admin,editor,gm'), 11 'add' => array('allow'=> 'admin,editor'), 12 ), 13 );
Acl中预定义的角色常量如下:
ACL_EVERYONE = 'acl_everyone'; // 所有用户
ACL_NULL = 'acl_null'; // 未设置
ACL_NO_ROLE = 'acl_no_role'; // 没有角色用户
ACL_HAS_ROLE = 'acl_has_role'; // 有角色用户
ALL_CONTROLLERS = 'all_controllers'; // 表示所有控制器
ALL_ACTIONS = 'all_actions'; // 表示所有控制器内的方法
4、测试一下,创建一个testacl.php文件,
1 <?php 2 class Testacl extends Auth_Controller { 3 function __construct() { 4 parent::__construct(); 5 } 6 7 function index() { 8 echo "index method"; 9 } 10 11 function add() { 12 echo "添加页,只有管理员和编辑有权限操作"; 13 } 14 }
运行结果:在MY_Controller中我们假设了role=“gm”,
所以访问http://localhost/CodeIgniter_2.1.1/index.php?c=testacl&m=index时输出 "index method"
而访问:http://localhost/CodeIgniter_2.1.1/index.php?c=testacl&m=add时输出 “无权限”。