1.最近在深入Thinkphp,发现版本已经到了3.2.3了,很多网上下载的代码都是老版本3.1,3.0的。3.2多了命名空间,而且一些核心类库,第三方库都放在Thinkphp/Library下,早此文件夹下的子目录自动注册了命名空间。Rbac封装类放在了/Org/Util/Rbac.class.php,没错,文件重新排版后我觉得清醒多了。如:
2.接下来打开这个文件哈,很复杂,大家请脑补,可参考:http://www.lyblog.net/2014/552.html
1 <?php 2 // +---------------------------------------------------------------------- 3 // | ThinkPHP [ WE CAN DO IT JUST THINK IT ] 4 // +---------------------------------------------------------------------- 5 // | Copyright (c) 2009 http://thinkphp.cn All rights reserved. 6 // +---------------------------------------------------------------------- 7 // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) 8 // +---------------------------------------------------------------------- 9 // | Author: liu21st <liu21st@gmail.com> 10 // +---------------------------------------------------------------------- 11 namespace Org/Util; 12 use Think/Db; 13 /** 14 +------------------------------------------------------------------------------ 15 * 基于角色的数据库方式验证类 16 +------------------------------------------------------------------------------ 17 */ 18 // 配置文件增加设置 19 // USER_AUTH_ON 是否需要认证 20 // USER_AUTH_TYPE 认证类型 21 // USER_AUTH_KEY 认证识别号 22 // REQUIRE_AUTH_MODULE 需要认证模块 23 // NOT_AUTH_MODULE 无需认证模块 24 // USER_AUTH_GATEWAY 认证网关 25 // RBAC_DB_DSN 数据库连接DSN 26 // RBAC_ROLE_TABLE 角色表名称 27 // RBAC_USER_TABLE 用户表名称 28 // RBAC_ACCESS_TABLE 权限表名称 29 // RBAC_NODE_TABLE 节点表名称 30 /* 31 -- -------------------------------------------------------- 32 CREATE TABLE IF NOT EXISTS `think_access` ( 33 `role_id` smallint(6) unsigned NOT NULL, 34 `node_id` smallint(6) unsigned NOT NULL, 35 `level` tinyint(1) NOT NULL, 36 `module` varchar(50) DEFAULT NULL, 37 KEY `groupId` (`role_id`), 38 KEY `nodeId` (`node_id`) 39 ) ENGINE=MyISAM DEFAULT CHARSET=utf8; 40 41 CREATE TABLE IF NOT EXISTS `think_node` ( 42 `id` smallint(6) unsigned NOT NULL AUTO_INCREMENT, 43 `name` varchar(20) NOT NULL, 44 `title` varchar(50) DEFAULT NULL, 45 `status` tinyint(1) DEFAULT '0', 46 `remark` varchar(255) DEFAULT NULL, 47 `sort` smallint(6) unsigned DEFAULT NULL, 48 `pid` smallint(6) unsigned NOT NULL, 49 `level` tinyint(1) unsigned NOT NULL, 50 PRIMARY KEY (`id`), 51 KEY `level` (`level`), 52 KEY `pid` (`pid`), 53 KEY `status` (`status`), 54 KEY `name` (`name`) 55 ) ENGINE=MyISAM DEFAULT CHARSET=utf8; 56 57 CREATE TABLE IF NOT EXISTS `think_role` ( 58 `id` smallint(6) unsigned NOT NULL AUTO_INCREMENT, 59 `name` varchar(20) NOT NULL, 60 `pid` smallint(6) DEFAULT NULL, 61 `status` tinyint(1) unsigned DEFAULT NULL, 62 `remark` varchar(255) DEFAULT NULL, 63 PRIMARY KEY (`id`), 64 KEY `pid` (`pid`), 65 KEY `status` (`status`) 66 ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ; 67 68 CREATE TABLE IF NOT EXISTS `think_role_user` ( 69 `role_id` mediumint(9) unsigned DEFAULT NULL, 70 `user_id` char(32) DEFAULT NULL, 71 KEY `group_id` (`role_id`), 72 KEY `user_id` (`user_id`) 73 ) ENGINE=MyISAM DEFAULT CHARSET=utf8; 74 */ 75 class Rbac { 76 // 认证方法 77 static public function authenticate($map,$model='') { 78 if(empty($model)) $model = C('USER_AUTH_MODEL'); 79 //使用给定的Map进行认证 80 return M($model)->where($map)->find(); 81 } 82 83 //用于检测用户权限的方法,并保存到Session中 84 static function saveAccessList($authId=null) { 85 if(null===$authId) $authId = $_SESSION[C('USER_AUTH_KEY')]; 86 // 如果使用普通权限模式,保存当前用户的访问权限列表 87 // 对管理员开发所有权限 88 if(C('USER_AUTH_TYPE') !=2 && !$_SESSION[C('ADMIN_AUTH_KEY')] ) 89 $_SESSION['_ACCESS_LIST'] = self::getAccessList($authId); 90 return ; 91 } 92 93 // 取得模块的所属记录访问权限列表 返回有权限的记录ID数组 94 static function getRecordAccessList($authId=null,$module='') { 95 if(null===$authId) $authId = $_SESSION[C('USER_AUTH_KEY')]; 96 if(empty($module)) $module = CONTROLLER_NAME; 97 //获取权限访问列表 98 $accessList = self::getModuleAccessList($authId,$module); 99 return $accessList; 100 } 101 102 //检查当前操作是否需要认证 103 static function checkAccess() { 104 //如果项目要求认证,并且当前模块需要认证,则进行权限认证 105 if( C('USER_AUTH_ON') ){ 106 $_module = array(); 107 $_action = array(); 108 if("" != C('REQUIRE_AUTH_MODULE')) { 109 //需要认证的模块 110 $_module['yes'] = explode(',',strtoupper(C('REQUIRE_AUTH_MODULE'))); 111 }else { 112 //无需认证的模块 113 $_module['no'] = explode(',',strtoupper(C('NOT_AUTH_MODULE'))); 114 } 115 //检查当前模块是否需要认证 116 if((!empty($_module['no']) && !in_array(strtoupper(CONTROLLER_NAME),$_module['no'])) || (!empty($_module['yes']) && in_array(strtoupper(CONTROLLER_NAME),$_module['yes']))) { 117 if("" != C('REQUIRE_AUTH_ACTION')) { 118 //需要认证的操作 119 $_action['yes'] = explode(',',strtoupper(C('REQUIRE_AUTH_ACTION'))); 120 }else { 121 //无需认证的操作 122 $_action['no'] = explode(',',strtoupper(C('NOT_AUTH_ACTION'))); 123 } 124 //检查当前操作是否需要认证 125 if((!empty($_action['no']) && !in_array(strtoupper(ACTION_NAME),$_action['no'])) || (!empty($_action['yes']) && in_array(strtoupper(ACTION_NAME),$_action['yes']))) { 126 return true; 127 }else { 128 return false; 129 } 130 }else { 131 return false; 132 } 133 } 134 return false; 135 } 136 137 // 登录检查 138 static public function checkLogin() { 139 //检查当前操作是否需要认证 140 if(self::checkAccess()) { 141 //检查认证识别号 142 if(!$_SESSION[C('USER_AUTH_KEY')]) { 143 if(C('GUEST_AUTH_ON')) { 144 // 开启游客授权访问 145 if(!isset($_SESSION['_ACCESS_LIST'])) 146 // 保存游客权限 147 self::saveAccessList(C('GUEST_AUTH_ID')); 148 }else{ 149 // 禁止游客访问跳转到认证网关 150 redirect(PHP_FILE.C('USER_AUTH_GATEWAY')); 151 } 152 } 153 } 154 return true; 155 } 156 157 //权限认证的过滤器方法 158 static public function AccessDecision($appName=MODULE_NAME) { 159 //检查是否需要认证 160 if(self::checkAccess()) { 161 //存在认证识别号,则进行进一步的访问决策 162 $accessGuid = md5($appName.CONTROLLER_NAME.ACTION_NAME); //可以访问的session 163 if(empty($_SESSION[C('ADMIN_AUTH_KEY')])) { 164 if(C('USER_AUTH_TYPE')==2) { 165 //加强验证和即时验证模式 更加安全 后台权限修改可以即时生效 166 //通过数据库进行访问检查 167 $accessList = self::getAccessList($_SESSION[C('USER_AUTH_KEY')]); 168 }else { 169 // 如果是管理员或者当前操作已经认证过,无需再次认证 170 if( $_SESSION[$accessGuid]) { 171 return true; 172 } 173 //登录验证模式,比较登录后保存的权限访问列表 174 $accessList = $_SESSION['_ACCESS_LIST']; 175 } 176 //判断是否为组件化模式,如果是,验证其全模块名 177 if(!isset($accessList[strtoupper($appName)][strtoupper(CONTROLLER_NAME)][strtoupper(ACTION_NAME)])) { 178 $_SESSION[$accessGuid] = false; 179 return false; 180 } 181 else { 182 $_SESSION[$accessGuid] = true; 183 } 184 }else{ 185 //管理员无需认证 186 return true; 187 } 188 } 189 return true; 190 } 191 192 /** 193 +---------------------------------------------------------- 194 * 取得当前认证号的所有权限列表 195 +---------------------------------------------------------- 196 * @param integer $authId 用户ID 197 +---------------------------------------------------------- 198 * @access public 199 +---------------------------------------------------------- 200 */ 201 static public function getAccessList($authId) { 202 // Db方式权限数据 203 $db = Db::getInstance(C('RBAC_DB_DSN')); 204 $table = array('role'=>C('RBAC_ROLE_TABLE'),'user'=>C('RBAC_USER_TABLE'),'access'=>C('RBAC_ACCESS_TABLE'),'node'=>C('RBAC_NODE_TABLE')); 205 $sql = "select node.id,node.name from ". 206 $table['role']." as role,". 207 $table['user']." as user,". 208 $table['access']." as access ,". 209 $table['node']." as node ". 210 "where user.user_id='{$authId}' and user.role_id=role.id and ( access.role_id=role.id or (access.role_id=role.pid and role.pid!=0 ) ) and role.status=1 and access.node_id=node.id and node.level=1 and node.status=1"; 211 $apps = $db->query($sql); 212 $access = array(); 213 foreach($apps as $key=>$app) { 214 $appId = $app['id']; 215 $appName = $app['name']; 216 // 读取项目的模块权限 217 $access[strtoupper($appName)] = array(); 218 $sql = "select node.id,node.name from ". 219 $table['role']." as role,". 220 $table['user']." as user,". 221 $table['access']." as access ,". 222 $table['node']." as node ". 223 "where user.user_id='{$authId}' and user.role_id=role.id and ( access.role_id=role.id or (access.role_id=role.pid and role.pid!=0 ) ) and role.status=1 and access.node_id=node.id and node.level=2 and node.pid={$appId} and node.status=1"; 224 $modules = $db->query($sql); 225 // 判断是否存在公共模块的权限 226 $publicAction = array(); 227 foreach($modules as $key=>$module) { 228 $moduleId = $module['id']; 229 $moduleName = $module['name']; 230 if('PUBLIC'== strtoupper($moduleName)) { 231 $sql = "select node.id,node.name from ". 232 $table['role']." as role,". 233 $table['user']." as user,". 234 $table['access']." as access ,". 235 $table['node']." as node ". 236 "where user.user_id='{$authId}' and user.role_id=role.id and ( access.role_id=role.id or (access.role_id=role.pid and role.pid!=0 ) ) and role.status=1 and access.node_id=node.id and node.level=3 and node.pid={$moduleId} and node.status=1"; 237 $rs = $db->query($sql); 238 foreach ($rs as $a){ 239 $publicAction[$a['name']] = $a['id']; 240 } 241 unset($modules[$key]); 242 break; 243 } 244 } 245 // 依次读取模块的操作权限 246 foreach($modules as $key=>$module) { 247 $moduleId = $module['id']; 248 $moduleName = $module['name']; 249 $sql = "select node.id,node.name from ". 250 $table['role']." as role,". 251 $table['user']." as user,". 252 $table['access']." as access ,". 253 $table['node']." as node ". 254 "where user.user_id='{$authId}' and user.role_id=role.id and ( access.role_id=role.id or (access.role_id=role.pid and role.pid!=0 ) ) and role.status=1 and access.node_id=node.id and node.level=3 and node.pid={$moduleId} and node.status=1"; 255 $rs = $db->query($sql); 256 $action = array(); 257 foreach ($rs as $a){ 258 $action[$a['name']] = $a['id']; 259 } 260 // 和公共模块的操作权限合并 261 $action += $publicAction; 262 $access[strtoupper($appName)][strtoupper($moduleName)] = array_change_key_case($action,CASE_UPPER); 263 } 264 } 265 return $access; 266 } 267 268 // 读取模块所属的记录访问权限 269 static public function getModuleAccessList($authId,$module) { 270 // Db方式 271 $db = Db::getInstance(C('RBAC_DB_DSN')); 272 $table = array('role'=>C('RBAC_ROLE_TABLE'),'user'=>C('RBAC_USER_TABLE'),'access'=>C('RBAC_ACCESS_TABLE')); 273 $sql = "select access.node_id from ". 274 $table['role']." as role,". 275 $table['user']." as user,". 276 $table['access']." as access ". 277 "where user.user_id='{$authId}' and user.role_id=role.id and ( access.role_id=role.id or (access.role_id=role.pid and role.pid!=0 ) ) and role.status=1 and access.module='{$module}' and access.status=1"; 278 $rs = $db->query($sql); 279 $access = array(); 280 foreach ($rs as $node){ 281 $access[] = $node['node_id']; 282 } 283 return $access; 284 } 285 }
3.在你的配置文件中,如Application/Common/Config/config.php或者模块下的Application/Admin/Config/config.php下补入:
1 'USER_AUTH_ON' => true, // 支持权限检查? 2 'USER_AUTH_TYPE' => 1, // 默认认证类型 1 登录认证 2 实时认证 3 'USER_AUTH_KEY' => 'authId', // 用户认证SESSION标记 4 'ADMIN_AUTH_KEY' => 'administrator', // session里有这个管理员不需要认证 5 'ADMINISTRATOR' => 'admin', 6 'USER_AUTH_MODEL' => 'User', // 默认验证数据表模型 7 'AUTH_PWD_ENCODER' => 'md5', // 用户认证密码加密方式 8 'USER_AUTH_GATEWAY' => '/Public/login', // 默认认证网关 9 'NOT_AUTH_MODULE' => 'Public', // 默认无需认证模块 10 'REQUIRE_AUTH_MODULE' => '', // 默认需要认证模块 11 'NOT_AUTH_ACTION' => '', // 默认无需认证操作 12 'REQUIRE_AUTH_ACTION' => '', // 默认需要认证操作 13 'GUEST_AUTH_ON' => false, // 是否开启游客授权访问 14 'GUEST_AUTH_ID' => 0, // 游客的用户ID 15 16 'RBAC_ERROR_PAGE' => '/thinkphps/Public/error404.html'
4.建一个公共控制器,如CommomController.class.php,里面有个构造函数进行权限验证。然后呢,其他控制器都继承它,这样都行了。或者可以在行为里面定义,然后监听,这个我就不贴了。如:
<?php // @function 公共控制器 namespace Admin/Controller; use Think/Controller; class CommonController extends Controller { function _initialize() { // 用户权限检查 if (C ( 'USER_AUTH_ON' ) && ! in_array ( MODULE_NAME, explode ( ',', C ( 'NOT_AUTH_MODULE' ) ) )) { //1.如果需要验证 if (! /Org/Util/Rbac::AccessDecision ()) { // 2.没有登陆 if (! $_SESSION [C ( 'USER_AUTH_KEY' )]) { // 3.游客可访问 if(C('GUEST_AUTH_ON')) { // 4.游客授权 if(!isset($_SESSION['_ACCESS_LIST'])) // 保存游客权限 /Org/Util/Rbac::saveAccessList(C('GUEST_AUTH_ID')); }else{ // 5.无登陆,禁止游客访问,无权限页面 $this->error ( L ( '_VALID_ACCESS_' ) ); } } // 6.登陆,没有权限, 如果有错误页面则定向 if (C ( 'RBAC_ERROR_PAGE' )) { // 定义权限错误页面 redirect ( C ( 'RBAC_ERROR_PAGE' ) ); } //7.没有定义错误页面定向,跳到登陆页面 else{ redirect(PHP_FILE.C('USER_AUTH_GATEWAY')); } } } }
5.具体验证的过程已经在上述给出,大家可以吐槽我。
这个Rbac,基于角色访问控制还挺麻烦的,因为你要写Node数据表规定那个操作不能访问,要写很多哦,而且开启游客访问,游客ID是0,你也要手动写进去。
听说还有一种Auth验证什么的。
6.不要问我这是什么,请脑补:
L ( '_VALID_ACCESS_' ) ;
PHP_FILE
我只能帮你这么多~~~