<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                決定某些控制器方法在某些情況下可以訪問,某些情況下不能訪問的這種控制行為叫做權限控制。ThinkPHP提供了基于RBAC和基于AUTH的兩種權限。 # RBAC ## 概念 基于角色的訪問控制(Role-Based Access Control)。它作為傳統訪問控制(自主訪問,強制訪問)的有前景的代替受到廣泛的關注。 在RBAC中,權限與角色相關聯,用戶通過成為適當角色的成員而得到這些角色的權限。這就極大地簡化了權限的管理。 在一個組織中,角色是為了完成各種工作而創造,用戶則依據它的責任和資格來被指派相應的角色,用戶可以很容易地從一個角色被指派到另一個角色。角色可依新的需求和系統的合并而賦予新的權限,而權限也可根據需要而從某角色中回收。角色與角色的關系可以建立起來以囊括更廣泛的客觀情況。 ## 作用 區分不同角色的可見資源、可操作資源。如老板可以控制最大、財務可以看見并處理財務相關的報表。研發人員可以看見整個系統的組成模塊、導航和配置之類的。哪些操作誰該直接處理,這些操作的權限就應該給哪個角色。 ## 組成 ### 安全攔截器 RBAC::checkAccess 方法返回當前請求的操縱是否需要認證。 ### 認證管理器 RBAC::authenticate 識別不同的身份,你的用戶名、密碼、權限是否在授權范圍內。 ### 決策訪問管理器 RBAC::AccessDecision 必須同認證管理器一同使用 分為即時模式和登錄模式。即時模式指修改了權限后,下次操作訪問時權限檢測立即生效,登錄模式則必須退出后再登錄權限列表才會是新的。其實就是一個是每次讀表查詢出數據,一個是讀取之前登錄時獲取的權限列表的session緩存。 ### 運行身份管理器 單身份、多身份管理B/S。單身份指一個用戶只有一個角色,多身份指一個用戶具有多個身份,并且某個操作權限要求極高必須多個身份的權限才能使用。所以多身份管理一般不存在。 ## 原理 1. 判斷當前的操作(項目【應用】,模塊、動作(操作)是否需要認證)對應節點(Node表里和配置里和AUTH相關的配置如`NOT_AUTH_MODULE`、`REQUIRE_AUTH_MODULE`、`NOT_AUTH_ACTION`、`REQUIRE_AUTH_ACTION`也就是需要認證的模塊、不需要認證的模塊、需要認證的操作和不需要認證的操作啦)。 2. 如果需要認證(判斷用戶是否登錄,如果沒登錄-->跳至委托認證管理器驗證身份,判斷用戶是否有權限訪問如果沒權限--直接跳至無權訪問頁面) 3. 委托認證來驗證用戶身份 4. 獲取該用戶的權限列表 5. 判斷用戶是否有權限訪問 ![document/2015-09-14/55f5a0d77bf0c](http://box.kancloud.cn/document_2015-09-14_55f5a0d77bf0c.png) ## 如何使用 為了方便大家更好的理解,我在參考了[http://www.thinkphp.cn/code/714.html](http://www.thinkphp.cn/code/714.html) 源碼后,將年久失修的ThinkPHP RBAC示列美化增強了一下。我會結合示列給大家講解如何正確使用ThinkPHP的RBAC。 ### 確認類庫文件存在 ThinkPHP中使用RBAC挺簡單,只要你的第三方類庫里有RBAC類文件,![document/2015-09-09/55efc743ce24f](http://box.kancloud.cn/document_2015-09-09_55efc743ce24f.png)。那么在你要使用RBAC的類的控制器里寫上命名空間 `use Org\Util\Rbac;` 就可以使用RBAC::checkAccess 的這些方法了。 當然這只是類文件的準備,要使用RBAC我們得準備5張表。 ### 建表 user(用戶)、role(角色)、node(節點)、role_user(角色用戶關聯表)、access(角色權限關聯表) #### user用戶表 除了必備的 id、account、password 之外,其他字段和權限無關。 #### role角色表 id、name、status、remark備注 是必要的, 其他的也可以不要。有可能角色存在上下級關系,我覺得很少會用到。 #### role_user 角色關聯表 role_id、user_id 只用來關聯的 #### node 節點 id、name、title、status、remark 這個是rbac 最終認證的最小單位, 其實最小單位是 APP_NAME.Controller_NAME.ACTION_NAME。 #### access 權限關聯表 role_id、node_id、level、pid 用于存放每個節點可以操作的節點id 以上表的建表sql RBAC類里注釋已經幫我們準備好了,注意原文件access表的少了一個pid字段。 下面是建表sql: ~~~ CREATE TABLE IF NOT EXISTS `rbac_access` ( `role_id` smallint(6) unsigned NOT NULL, `node_id` smallint(6) unsigned NOT NULL, `level` tinyint(1) NOT NULL, `pid` smallint(6) NOT NULL, `module` varchar(50) DEFAULT NULL, KEY `groupId` (`role_id`), KEY `nodeId` (`node_id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; -- -- 轉存表中的數據 `rbac_access` -- INSERT INTO `rbac_access` (`role_id`, `node_id`, `level`, `pid`, `module`) VALUES (2, 1, 1, 0, NULL), (2, 40, 2, 1, NULL), (2, 30, 2, 1, NULL), (3, 1, 1, 0, NULL), (2, 69, 2, 1, NULL), (2, 50, 3, 40, NULL), (3, 50, 3, 40, NULL), (1, 50, 3, 40, NULL), (3, 7, 2, 1, NULL), (3, 39, 3, 30, NULL), (2, 39, 3, 30, NULL), (2, 49, 3, 30, NULL), (4, 1, 1, 0, NULL), (4, 2, 2, 1, NULL), (4, 3, 2, 1, NULL), (4, 4, 2, 1, NULL), (4, 5, 2, 1, NULL), (4, 6, 2, 1, NULL), (4, 7, 2, 1, NULL), (4, 11, 2, 1, NULL), (5, 25, 1, 0, NULL), (5, 51, 2, 25, NULL), (1, 1, 1, 0, NULL), (1, 39, 3, 30, NULL), (1, 69, 2, 1, NULL), (1, 30, 2, 1, NULL), (1, 40, 2, 1, NULL), (1, 49, 3, 30, NULL), (3, 69, 2, 1, NULL), (3, 30, 2, 1, NULL), (3, 40, 2, 1, NULL), (1, 37, 3, 30, NULL), (1, 36, 3, 30, NULL), (1, 35, 3, 30, NULL), (1, 34, 3, 30, NULL), (1, 33, 3, 30, NULL), (1, 32, 3, 30, NULL), (1, 31, 3, 30, NULL), (2, 32, 3, 30, NULL), (2, 31, 3, 30, NULL), (7, 1, 1, 0, NULL), (7, 30, 2, 1, NULL), (7, 40, 2, 1, NULL), (7, 69, 2, 1, NULL), (7, 50, 3, 40, NULL), (7, 39, 3, 30, NULL), (7, 49, 3, 30, NULL); CREATE TABLE IF NOT EXISTS `rbac_form` ( `id` smallint(4) unsigned NOT NULL AUTO_INCREMENT, `title` varchar(255) NOT NULL, `content` varchar(255) NOT NULL, `create_time` int(11) unsigned NOT NULL, `status` tinyint(1) unsigned NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ; CREATE TABLE IF NOT EXISTS `rbac_group` ( `id` smallint(3) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(25) NOT NULL, `title` varchar(50) NOT NULL, `create_time` int(11) unsigned NOT NULL, `update_time` int(11) unsigned NOT NULL DEFAULT '0', `status` tinyint(1) unsigned NOT NULL DEFAULT '0', `sort` smallint(3) unsigned NOT NULL DEFAULT '0', `show` tinyint(1) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=6 ; -- -- 轉存表中的數據 `rbac_group` -- INSERT INTO `rbac_group` (`id`, `name`, `title`, `create_time`, `update_time`, `status`, `sort`, `show`) VALUES (2, 'App', '應用中心', 1222841259, 0, 1, 0, 0); CREATE TABLE IF NOT EXISTS `rbac_node` ( `id` smallint(6) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(20) NOT NULL, `title` varchar(50) DEFAULT NULL, `status` tinyint(1) DEFAULT '0', `remark` varchar(255) DEFAULT NULL, `sort` smallint(6) unsigned DEFAULT NULL, `pid` smallint(6) unsigned NOT NULL, `level` tinyint(1) unsigned NOT NULL, `type` tinyint(1) NOT NULL DEFAULT '0', `group_id` tinyint(3) unsigned DEFAULT '0', PRIMARY KEY (`id`), KEY `level` (`level`), KEY `pid` (`pid`), KEY `status` (`status`), KEY `name` (`name`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=83 ; -- -- 轉存表中的數據 `rbac_node` -- INSERT INTO `rbac_node` (`id`, `name`, `title`, `status`, `remark`, `sort`, `pid`, `level`, `type`, `group_id`) VALUES (49, 'read', '查看', 1, '', NULL, 30, 3, 0, 0), (40, 'Index', '默認模塊', 1, '', 1, 1, 2, 0, 0), (39, 'index', '列表', 1, '', NULL, 30, 3, 0, 0), (37, 'resume', '恢復', 1, '', NULL, 30, 3, 0, 0), (36, 'forbid', '禁用', 1, '', NULL, 30, 3, 0, 0), (35, 'foreverdelete', '刪除', 1, '', NULL, 30, 3, 0, 0), (34, 'update', '更新', 1, '', NULL, 30, 3, 0, 0), (33, 'edit', '編輯', 1, '', NULL, 30, 3, 0, 0), (32, 'insert', '寫入', 1, '', NULL, 30, 3, 0, 0), (31, 'add', '新增', 1, '', NULL, 30, 3, 0, 0), (30, 'Public', '公共模塊', 1, '', 2, 1, 2, 0, 0), (69, 'Form', '數據管理', 1, '', 1, 1, 2, 0, 2), (7, 'User', '后臺用戶', 1, '', 4, 1, 2, 0, 2), (6, 'Role', '角色管理', 1, '', 3, 1, 2, 0, 2), (2, 'Node', '節點管理', 1, '', 2, 1, 2, 0, 2), (1, 'App', 'Rbac后臺管理', 1, '', NULL, 0, 1, 0, 0), (50, 'main', '空白首頁', 1, '', NULL, 40, 3, 0, 0); CREATE TABLE IF NOT EXISTS `rbac_role` ( `id` smallint(6) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(20) NOT NULL, `pid` smallint(6) DEFAULT NULL, `status` tinyint(1) unsigned DEFAULT NULL, `remark` varchar(255) DEFAULT NULL, `ename` varchar(5) DEFAULT NULL, `create_time` int(11) unsigned NOT NULL, `update_time` int(11) unsigned NOT NULL, PRIMARY KEY (`id`), KEY `parentId` (`pid`), KEY `ename` (`ename`), KEY `status` (`status`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=8 ; -- -- 轉存表中的數據 `rbac_role` -- INSERT INTO `rbac_role` (`id`, `name`, `pid`, `status`, `remark`, `ename`, `create_time`, `update_time`) VALUES (1, '領導組', 0, 1, '', '', 1208784792, 1254325558), (2, '員工組', 0, 1, '', '', 1215496283, 1254325566), (7, '演示組', 0, 1, '', NULL, 1254325787, 0); CREATE TABLE IF NOT EXISTS `rbac_role_user` ( `role_id` mediumint(9) unsigned DEFAULT NULL, `user_id` char(32) DEFAULT NULL, KEY `group_id` (`role_id`), KEY `user_id` (`user_id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; -- -- 轉存表中的數據 `rbac_role_user` -- INSERT INTO `rbac_role_user` (`role_id`, `user_id`) VALUES (4, '27'), (4, '26'), (4, '30'), (5, '31'), (3, '22'), (3, '1'), (1, '4'), (2, '3'), (7, '2'); CREATE TABLE IF NOT EXISTS `rbac_user` ( `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT, `account` varchar(64) NOT NULL, `nickname` varchar(50) NOT NULL, `password` char(32) NOT NULL, `bind_account` varchar(50) NOT NULL, `last_login_time` int(11) unsigned DEFAULT '0', `last_login_ip` varchar(40) DEFAULT NULL, `login_count` mediumint(8) unsigned DEFAULT '0', `verify` varchar(32) DEFAULT NULL, `email` varchar(50) NOT NULL, `remark` varchar(255) NOT NULL, `create_time` int(11) unsigned NOT NULL, `update_time` int(11) unsigned NOT NULL, `status` tinyint(1) DEFAULT '0', `type_id` tinyint(2) unsigned DEFAULT '0', `info` text NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `account` (`account`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=35 ; -- -- 轉存表中的數據 `rbac_user` -- INSERT INTO `rbac_user` (`id`, `account`, `nickname`, `password`, `bind_account`, `last_login_time`, `last_login_ip`, `login_count`, `verify`, `email`, `remark`, `create_time`, `update_time`, `status`, `type_id`, `info`) VALUES (1, 'admin', '管理員', '21232f297a57a5a743894a0e4a801fc3', '', 1326335612, '127.0.0.1', 888, '8888', 'liu21st@gmail.com', '備注信息', 1222907803, 1326266696, 1, 0, ''), (2, 'demo', '演示', 'fe01ce2a7fbac8fafaed7c982a04e229', '', 1254326091, '127.0.0.1', 90, '8888', '', '', 1239783735, 1254325770, 1, 0, ''), (3, 'member', '員工', 'aa08769cdcb26674c6706093503ff0a3', '', 1326266720, '127.0.0.1', 17, '', '', '', 1253514375, 1254325728, 1, 0, ''), (4, 'leader', '領導', 'c444858e0aaeb727da73d2eae62321ad', '', 1254325906, '127.0.0.1', 15, '', '', '領導', 1253514575, 1254325705, 1, 0, ''); ~~~ 這sql可以見我的examples 里rbac應用下的rabc.sql。 ![document/2015-09-09/55efcf6e00519](http://box.kancloud.cn/document_2015-09-09_55efcf6e00519.png) 為了不區分不同應用,我將表前綴定為rbac_。 各個表的關系圖: ![document/2015-09-14/55f5ac519d89c](http://box.kancloud.cn/document_2015-09-14_55f5ac519d89c.png) ### 更改配置 將RBAC類注釋里的配置拿到Home的conf.php中, ~~~ 'USER_AUTH_ON' => true, 'USER_AUTH_TYPE' => 2, // 默認認證類型 1 登錄認證 2 實時認證 'USER_AUTH_KEY' => 'authId', // 用戶認證SESSION標記 'ADMIN_AUTH_KEY' => 'administrator', 'USER_AUTH_MODEL' => 'User', // 默認驗證數據表模型 'AUTH_PWD_ENCODER' => 'md5', // 用戶認證密碼加密方式 'USER_AUTH_GATEWAY' => '/Public/login',// 默認認證網關 'NOT_AUTH_MODULE' => 'Public', // 默認無需認證模塊 'REQUIRE_AUTH_MODULE' => '', // 默認需要認證模塊 'NOT_AUTH_ACTION' => '', // 默認無需認證操作 'REQUIRE_AUTH_ACTION' => '', // 默認需要認證操作 'GUEST_AUTH_ON' => false, // 是否開啟游客授權訪問 'GUEST_AUTH_ID' => 0, // 游客的用戶ID 'DB_LIKE_FIELDS' => 'title|remark', 'RBAC_ROLE_TABLE' => 'rbac_role', 'RBAC_USER_TABLE' => 'rbac_role_user', 'RBAC_ACCESS_TABLE' => 'rbac_access', 'RBAC_NODE_TABLE' => 'rbac_node', ~~~ 需要注意的是,用戶表如果你業務里不是User需要修改寫,其他4張表也按實際情況來寫真實表名。 ### 添加登錄后權限獲取緩存 ![document/2015-09-09/55efd5f152cf9](http://box.kancloud.cn/document_2015-09-09_55efd5f152cf9.png) ThinkPHP的RBAC 權限列表依賴于SESSION。并且權限是在登錄后獲取訪問權限列表的。 我們看下登錄方法,到底做了什么。 ~~~ //登錄頁面 public function login(){ if(IS_POST){ if(empty($_POST['username'])) { $this->error('帳號錯誤!'); }elseif (empty($_POST['password'])){ $this->error('密碼必須!'); } //生成認證條件 $map = array(); // 支持使用綁定帳號登錄 $map['account'] = $_POST['username']; $map["status"] = array('gt',0); //使用用戶名、密碼和狀態的方式進行認證 $authInfo = RBAC::authenticate($map); if(false === $authInfo) { $this->error('帳號不存在或已禁用!'); }else { if($authInfo['password'] != md5($_POST['password'])) { $this->error('密碼錯誤!'); } $_SESSION[C('USER_AUTH_KEY')] = $authInfo['id']; if($authInfo['username'] == C('ADMIN_AUTH_KEY')) { $_SESSION['administrator'] = true; } //保存登錄信息 $User = M('User'); $ip = get_client_ip(); $time = time(); $data = array(); $data['id'] = $authInfo['id']; $data['last_login_time'] = $time; $data['login_count'] = array('exp','login_count+1'); $data['last_login_ip'] = $ip; $User->save($data); // 緩存訪問權限 RBAC::saveAccessList(); $this->success('登錄成功!', U('/')); } }else{ $this->display(); } } ~~~ 1. 顯示用認證管理器獲取user表里存在的對應用戶信息。 //生成認證條件 ~~~ $map = array(); // 支持使用綁定帳號登錄 $map['account'] = $_POST['username']; $map["status"] = array('gt',0); //使用用戶名、密碼和狀態的方式進行認證 $authInfo = RBAC::authenticate($map); ~~~ 2. 然后判斷賬號是否存在、密碼是否正確。身份存在的話將用戶表id賦值給`$_SESSION[C('USER_AUTH_KEY')]`,并判斷是否是超級管理員 。 ~~~ if($authInfo['username'] == C('ADMIN_AUTH_KEY')) { $_SESSION['administrator'] = true; } ~~~ 3. 最關鍵的一步,緩存權限,如果是即時模式,緩存無效,最好寫上,方便認證模式切換。 ~~~ // 緩存訪問權限 RBAC::saveAccessList(); ~~~ 看下saveAccessList方法: ~~~ //用于檢測用戶權限的方法,并保存到Session中 static function saveAccessList($authId=null) { if(null===$authId) $authId = $_SESSION[C('USER_AUTH_KEY')]; // 如果使用普通權限模式,保存當前用戶的訪問權限列表 // 對管理員開發所有權限 if(C('USER_AUTH_TYPE') !=2 && !$_SESSION[C('ADMIN_AUTH_KEY')] ) $_SESSION['_ACCESS_LIST'] = self::getAccessList($authId); return ; } ~~~ 非即時模式、并且不是超級管理員時才更新session。 我們再看下getAccessList方法。 ~~~ static public function getAccessList($authId) { // Db方式權限數據 $db = Db::getInstance(C('RBAC_DB_DSN')); $table = array('role'=>C('RBAC_ROLE_TABLE'),'user'=>C('RBAC_USER_TABLE'),'access'=>C('RBAC_ACCESS_TABLE'),'node'=>C('RBAC_NODE_TABLE')); $sql = "select node.id,node.name from ". $table['role']." as role,". $table['user']." as user,". $table['access']." as access ,". $table['node']." as node ". "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"; $apps = $db->query($sql); $access = array(); foreach($apps as $key=>$app) { $appId = $app['id']; $appName = $app['name']; // 讀取項目的模塊權限 $access[strtoupper($appName)] = array(); $sql = "select node.id,node.name from ". $table['role']." as role,". $table['user']." as user,". $table['access']." as access ,". $table['node']." as node ". "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"; $modules = $db->query($sql); // 判斷是否存在公共模塊的權限 $publicAction = array(); foreach($modules as $key=>$module) { $moduleId = $module['id']; $moduleName = $module['name']; if('PUBLIC'== strtoupper($moduleName)) { $sql = "select node.id,node.name from ". $table['role']." as role,". $table['user']." as user,". $table['access']." as access ,". $table['node']." as node ". "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"; $rs = $db->query($sql); foreach ($rs as $a){ $publicAction[$a['name']] = $a['id']; } unset($modules[$key]); break; } } // 依次讀取模塊的操作權限 foreach($modules as $key=>$module) { $moduleId = $module['id']; $moduleName = $module['name']; $sql = "select node.id,node.name from ". $table['role']." as role,". $table['user']." as user,". $table['access']." as access ,". $table['node']." as node ". "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"; $rs = $db->query($sql); $action = array(); foreach ($rs as $a){ $action[$a['name']] = $a['id']; } // 和公共模塊的操作權限合并 $action += $publicAction; $access[strtoupper($appName)][strtoupper($moduleName)] = array_change_key_case($action,CASE_UPPER); } } return $access; } ~~~ 其實過程就是先查出為當前用戶授權過的應用列表(或叫項目),然后再遍歷項目獲取授權過的模塊,最后遍歷模塊獲取授權過的操作。 最后得到的數組類似: ![2015-08-02/55be24d99842e](http://box.kancloud.cn/2015-08-02_55be24d99842e.png) 最里層的都是節點id。這樣一個多維數組生成后,緩存到session里。 ### 將要進行權限判斷的控制器繼承Common控制器,自動綁上初始化進行**模塊/控制器/操作**對應的權限判斷 ~~~ <?php namespace Home\Controller; use Think\Controller; use Think\Page; use Org\Util\Rbac; //如果不需要權限驗證的控制器不要繼承它 class CommonController extends Controller{ //對權限的驗證和判斷 public function _initialize() { define('__URL__', __CONTROLLER__); // 用戶權限檢查 if (C('USER_AUTH_ON') && !in_array(MODULE_NAME, explode(',', C('NOT_AUTH_MODULE')))) { if (!RBAC::AccessDecision()) { //檢查認證識別后 if (!$_SESSION [C('USER_AUTH_KEY')]) { //跳轉到認證網關 redirect(PHP_FILE . C('USER_AUTH_GATEWAY')); } // 沒有權限 拋出錯誤 if (C('RBAC_ERROR_PAGE')) { // 定義權限錯誤頁面 redirect(C('RBAC_ERROR_PAGE')); } else { if (C('GUEST_AUTH_ON')) { $this->assign('jumpUrl', PHP_FILE . C('USER_AUTH_GATEWAY')); } // 提示錯誤信息 $this->error(L('_VALID_ACCESS_')); } } } } ~~~ 用RBAC類進行權限判斷: `if (C('USER_AUTH_ON') && !in_array(MODULE_NAME, explode(',', C('NOT_AUTH_MODULE')))) {` 當開啟RBAC判斷并且模塊不在無需認證模塊中時執行權限判斷。 用RBAC::AccessDecision() 決策訪問管理器進行判斷。 沒通過判斷,進行錯誤引導。 ~~~ if (!$_SESSION [C('USER_AUTH_KEY')]) { //跳轉到認證網關 redirect(PHP_FILE . C('USER_AUTH_GATEWAY')); } ~~~ 如果沒有認證的session表示沒登錄,跳到登錄頁面進行授權。 ~~~ // 沒有權限 拋出錯誤 if (C('RBAC_ERROR_PAGE')) { // 定義權限錯誤頁面 redirect(C('RBAC_ERROR_PAGE')); } else { if (C('GUEST_AUTH_ON')) { $this->assign('jumpUrl', PHP_FILE . C('USER_AUTH_GATEWAY')); } // 提示錯誤信息 $this->error(L('_VALID_ACCESS_')); } ~~~ 登錄了沒權限,如果定義了權限報錯頁面,去那。沒定義的話如果開啟游客模式,跳到登錄網關。沒有開游客訪問模式直接提示無權限。 所以這段代碼關鍵處在于判斷是否有權限,我們看AccessDescision方法。 ~~~ //權限認證的過濾器方法 static public function AccessDecision($appName=MODULE_NAME) { //檢查是否需要認證 if(self::checkAccess()) { //存在認證識別號,則進行進一步的訪問決策 $accessGuid = md5($appName.CONTROLLER_NAME.ACTION_NAME); if(empty($_SESSION[C('ADMIN_AUTH_KEY')])) { if(C('USER_AUTH_TYPE')==2) { //加強驗證和即時驗證模式 更加安全 后臺權限修改可以即時生效 //通過數據庫進行訪問檢查 $accessList = self::getAccessList($_SESSION[C('USER_AUTH_KEY')]); }else { // 如果是管理員或者當前操作已經認證過,無需再次認證 if( $_SESSION[$accessGuid]) { return true; } //登錄驗證模式,比較登錄后保存的權限訪問列表 $accessList = $_SESSION['_ACCESS_LIST']; } //判斷是否為組件化模式,如果是,驗證其全模塊名 if(!isset($accessList[strtoupper($appName)][strtoupper(CONTROLLER_NAME)][strtoupper(ACTION_NAME)])) { $_SESSION[$accessGuid] = false; return false; } else { $_SESSION[$accessGuid] = true; } }else{ //管理員無需認證 return true; } } return true; } ~~~ `self::checkAccess()`先判斷當前模塊下控制器下操作是否需要判斷。如果不需要咱直接通過返回true。 需要的話,先通過md5 將 `$appName.CONTROLLER_NAME.ACTION_NAME` 加密生成一個guid。 然后先通過session里ADMIN_AUTH_KEY 判斷是不是超級管理員,超級管理員直接返回true,具有所有權限。 不是超級管理員。 ~~~ if(C('USER_AUTH_TYPE')==2) { //加強驗證和即時驗證模式 更加安全 后臺權限修改可以即時生效 //通過數據庫進行訪問檢查 $accessList = self::getAccessList($_SESSION[C('USER_AUTH_KEY')]); }else { // 如果是管理員或者當前操作已經認證過,無需再次認證 if( $_SESSION[$accessGuid]) { return true; } //登錄驗證模式,比較登錄后保存的權限訪問列表 $accessList = $_SESSION['_ACCESS_LIST']; //判斷是否為組件化模式,如果是,驗證其全模塊名 } if(!isset($accessList[strtoupper($appName)][strtoupper(CONTROLLER_NAME)][strtoupper(ACTION_NAME)])) { $_SESSION[$accessGuid] = false; return false; } else { $_SESSION[$accessGuid] = true; } ~~~ 看當前認證模式是否是即時,即時就重新獲取,非即時的如果當前guid在session存在說明認證過了,不存在讀取訪問認證權限緩存列表。然后就是簡單的數組判斷看授權列表里有無當前[模塊][控制器][操作]的節點。 有就將session里guid 設為true,沒有就設為false。 老楊覺的這里寫的有問題,應該用isset判斷一下,如果$_SESSION[$accessGuid]賦過值,返回該session。沒賦過值,返回比較結果。 退出時清除整個session。 整個RBAC 認證流程就結束了。 ### RBAC附加功能的實現 其實RBAC的難點在于數據庫的理解和節點添加、角色授權。 從數據的先后順序上來說先是添加節點,然后新增用戶,新增角色,最后給角色授權。 #### 添加節點 這里注意的是節點是權限判斷的基礎數據,由于程序上的設計,必須保證上下級順序才能對應查詢時生成對應的多為數組供以判斷。所以其實大家只要記住先添加模塊再添加控制器最后添加操作即可。 我們先看一下老版的rbac示列: ![document/2015-09-09/55f000e3bcf34](http://box.kancloud.cn/document_2015-09-09_55f000e3bcf34.png) ![document/2015-09-09/55f000ff546ff](http://box.kancloud.cn/document_2015-09-09_55f000ff546ff.png) 點相應列表里的名稱 發布該節點的子節點。 老楊改后的呢: ![document/2015-09-09/55f001470a27a](http://box.kancloud.cn/document_2015-09-09_55f001470a27a.png) ![document/2015-09-09/55f001746a37a](http://box.kancloud.cn/document_2015-09-09_55f001746a37a.png) 看出來沒,少了一個分組,多了一個上級下啦,用以修改當前節點的上級pid。 如果是老示列,想移動一個節點。只能刪除了舊節點,去指定上級下新增元屬性一樣的節點了。 因為只是示列,老楊也沒去實現排序和搜索,其實大家都知道怎么實現。 這里我只特別說下這個下拉樹的實現。其他添加和老版的示列一樣,就是添加數據,帶個上下級pid而已。 在示列的NodeController 中, 寫了一個 _before_add 和_before_edit 用于編輯和顯示頁面獲取上級列表樹: ~~~ // 獲取配置類型 public function _before_add() { $model = M("Node"); $list = $model->where('status=1')->select(); $node_tree = D('Tree')->toFormatTree($list); $this->assign('node_tree', $node_tree); $this->assign('pid', I('pid', 1)); } public function _before_edit() { $this->_before_add(); } ~~~ 主要訣竅就在這個TreeModel里。 我們看把所有開啟狀態的節點查出來的列表怎么通過toFormatTree變成了有層級先后順序顯示的下拉。 ~~~ public function toFormatTree($list, $title = 'title', $pk = 'id', $pid = 'pid', $root = 0) { $list = list_to_tree($list, $pk, $pid, '_child', $root); $this->formatTree = array(); $this->_toFormatTree($list, 0, $title); return $this->formatTree; } ~~~ 首先將列表轉換為無限級tree。這個需求很常見。用了以前老板tp了擴展函數里的list_to_tree: ~~~ /** * 把返回的數據集轉換成Tree * @access public * @param array $list 要轉換的數據集 * @param string $pid parent標記字段 * @param string $level level標記字段 * @return array */ function list_to_tree($list, $pk = 'id', $pid = 'pid', $child = '_child', $root = 0) { // 創建Tree $tree = array(); if (is_array($list)) { // 創建基于主鍵的數組引用 $refer = array(); foreach ($list as $key => $data) { $refer[$data[$pk]] = & $list[$key]; } foreach ($list as $key => $data) { // 判斷是否存在parent $parentId = $data[$pid]; if ($root == $parentId) { $tree[] = & $list[$key]; } else { if (isset($refer[$parentId])) { $parent = & $refer[$parentId]; $parent[$child][] = & $list[$key]; } } } } return $tree; } ~~~ 轉換成樹以后,調用_toFormatTree進行格式轉換。 ~~~ /** * 將格式數組轉換為樹 * * @param array $list * @param integer $level 進行遞歸時傳遞用的參數 */ private $formatTree; //用于樹型數組完成遞歸格式的全局變量 private function _toFormatTree($list, $level = 0, $title = 'title') { foreach ($list as $key => $val) { $tmp_str = str_repeat(" ", $level * 1); $tmp_str.="└"; $val['level'] = $level; $val['title_show'] = $level == 0 ? $val[$title] : $tmp_str . $val[$title]; if (!array_key_exists('_child', $val)) { array_push($this->formatTree, $val); } else { $tmp_ary = $val['_child']; unset($val['_child']); array_push($this->formatTree, $val); $this->_toFormatTree($tmp_ary, $level + 1, $title); //進行下一層遞歸 } } return; } ~~~ 遍歷每條數據,根據level算出前面要補多少空格。如果沒有_child子樹就添加到formatTree屬性里。有的話遞歸調用_toFormatTree,傳入時講level+1了。這樣子級的前導空格始終比父級多一個。 #### 添加角色 ![document/2015-09-09/55f04ed4230df](http://box.kancloud.cn/document_2015-09-09_55f04ed4230df.png) 只是美化了一下。 #### 添加用戶 老版的是這樣的: ![document/2015-09-09/55f04e0869d61](http://box.kancloud.cn/document_2015-09-09_55f04e0869d61.png) 我修改之后多了選擇角色。 ![document/2015-09-09/55f04db9db861](http://box.kancloud.cn/document_2015-09-09_55f04db9db861.png) 并且在添加的時候已經自動加入了role_user表。 編輯時也先刪除了該user的所有role,保存最后編輯的那一個。 老版的角色綁定多個用戶目前來說完全用不到: ![document/2015-09-09/55f04e7ec6f17](http://box.kancloud.cn/document_2015-09-09_55f04e7ec6f17.png) #### 角色授權優化 老版的角色授權很麻煩,完成一個節點授權得經過3步。 先授權應用: ![document/2015-09-09/55f04f49373f7](http://box.kancloud.cn/document_2015-09-09_55f04f49373f7.png) 再授權模塊: ![document/2015-09-09/55f04fa04177a](http://box.kancloud.cn/document_2015-09-09_55f04fa04177a.png) 最后再選擇模塊,授權子級操作: ![document/2015-09-09/55f04fcc8dde3](http://box.kancloud.cn/document_2015-09-09_55f04fcc8dde3.png) 而老楊修改了只需要一步: ![document/2015-09-09/55f0504a3e59d](http://box.kancloud.cn/document_2015-09-09_55f0504a3e59d.png) 實現方法就是,顯示把模塊和對應操作遍歷出來,并且獲取對應角色的權限列表后,輸出json,給前端進行匹配勾選。將勾選的節點id保存在rule數組里傳遞。 保存時: ~~~ public function saveAccessList(){ $groupId = I('groupID'); $model = D('Role'); $apps = $model->getGroupAppList($groupId); $moduleList = $model->getGroupModuleList($groupId, $apps[0]['id']); foreach ($moduleList as $key => $module) { $model->delGroupAction($groupId, $module['id']); } $res = $model->setGroupActions($groupId, I('rule')); if($res) $this->success('更新成功'); else $this->error('更新失敗'); } ~~~ 先按模塊刪除權限列表。然后再保存該角色的可用操作節點列表。 getGroupModuleList和delGroupAction及setGroupModules這些都是官網老rbac示列里的方法。 ~~~ function getGroupModuleList($groupId,$appId) { $table = $this->tablePrefix.'access'; $rs = $this->db->query('select b.id,b.title,b.name from '.$table.' as a ,'.$this->tablePrefix.'node as b where a.node_id=b.id and b.pid='.$appId.' and a.role_id='.$groupId.' '); return $rs; } function setGroupModules($groupId,$moduleIdList) { if(empty($moduleIdList)) { return true; } if(is_array($moduleIdList)) { $moduleIdList = implode(',',$moduleIdList); } $where = 'a.id ='.$groupId.' AND b.id in('.$moduleIdList.')'; $rs = $this->db->execute('INSERT INTO '.$this->tablePrefix.'access (role_id,node_id,pid,level) SELECT a.id, b.id,b.pid,b.level FROM '.$this->tablePrefix.'role a, '.$this->tablePrefix.'node b WHERE '.$where); if($result===false) { return false; }else { return true; } } function delGroupAction($groupId,$moduleId) { $table = $this->tablePrefix.'access'; $result = $this->db->execute('delete from '.$table.' where level=3 and pid='.$moduleId.' and role_id='.$groupId); if($result===false) { return false; }else { return true; } } ~~~ 這樣一個角色授權在一個頁面內就可選擇并保存完畢了。不用來回切模塊、操作。 真正授權的苦力活就是添加節點了。其實那里可以優化用前端樹組件做到一個頁面ajax管理樹節點。大家自己深入吧。 詳情可以去示列里看rbac的源碼。我就不贅述了。 # AUTH Auth 類已經在ThinkPHP代碼倉庫中存在很久了,但是因為一直沒有出過它的教程, 很少人知道它, 它其實比RBAC更方便 。 RBAC是按節點進行認證的,如果要控制比節點更細的權限就有點困難了,比如頁面上面的操作按鈕, 我想判斷用戶權限來顯示這個按鈕, 如果沒有權限就不會顯示這個按鈕; 再比如我想按積分進行權限認證, 積分在0-100時能干什么, 在101-200時能干什么。 這些權限認證用RABC都很困難。 下面介紹 Auth權限認證, 它幾乎是全能的, 除了能進行節點認證, 上面說的RABC很難認證的兩種情況,它都能實現。 Auth權限認證是按規則進行認證。我先說說它的原理。 在數據庫中我們有 規則表(think_auth_rule) ,用戶組表(think_auth_group), 用戶組明顯表(think_auth_group_access) 我們在規則表中定義權限規則 , 在用戶組表中定義每個用戶組有哪些權限規則,在用戶組明顯表中 定義用戶所屬的用戶組。 下面舉例說明。 我們要判斷用戶是否有顯示一個操作按鈕的權限, 首先定義一個規則, 在規則表中添加一個名為 show_button 的規則。 然后在用戶組表添加一個用戶組,定義這個用戶組有show_button 的權限規則(think_auth_group表中rules字段存得時規則ID,多個以逗號隔開), 然后在用戶組明細表定義 UID 為1 的用戶 屬于剛才這個的這個用戶組。 ok,表數據定義好后, 判斷權限很簡單 ~~~ import('ORG.Util.Auth');//加載類庫 $auth=new Auth(); if($auth->check('show_button',1)){// 第一個參數是規則名稱,第二個參數是用戶UID //有顯示操作按鈕的權限 }else{ //沒有顯示操作按鈕的權限 } ~~~ Auth類同樣可以做像RBAC一樣的對節點進行認證。 我們只要將規則名稱,定義為節點名稱就行了。 和RABC一樣 在公共控制器CommonAction 中定義_initialize 方法, ~~~ <?php class CommonAction extends Action{ public function _initialize(){ import('ORG.Util.Auth');//加載類庫 $auth=new Auth(); if(!$auth->check(MODULE_NAME.'-'.ACTION_NAME,session('uid'))){ $this->error('你沒有權限'); } } } ~~~ 這時候我們可以在數據庫中添加的節點規則, 格式為: “控制器名稱-方法名稱” Auth 類 還可以多個規則一起認證 如: `$auth->check('rule1,rule2',uid); ` 表示 認證用戶只要有rule1的權限或rule2的權限,只要有一個規則的權限,認證返回結果就為true 即認證通過。 默認多個權限的關系是 “or” 關系,也就是說多個權限中,只要有個權限通過則通過。 我們也可以定義為 “and” 關系 `$auth->check('rule1,rule2',uid,'and'); ` 第三個參數指定為"and" 表示多個規則以and關系進行認證, 這時候多個規則同時通過認證 才有權限。只要一個規則沒有權限則就會返回false。 Auth認證,一個用戶可以屬于多個用戶組。 比如我們對 show_button這個規則進行認證, 用戶A 同時屬于 用戶組1 和用戶組2 兩個用戶組 , 用戶組1 沒有show_button 規則權限, 但如果用戶組2 有show_button 規則權限,則一樣會權限認證通過。 `$auth->getGroups(uid)` 通過上面代碼,可以獲得用戶所屬的所有用戶組,方便我們在網站上面顯示。 Auth類還可以按用戶屬性進行判斷權限, 比如 按照用戶積分進行判斷, 假設我們的用戶表 (think_members) 有字段 score 記錄了用戶積分。 我在規則表添加規則時,定義規則表的condition 字段,condition字段是規則條件, 默認為空 表示沒有附加條件, 用戶組中只有規則 就通過認證。 如果定義了 condition字段, 用戶組中有規則 不一定能通過認證, 程序還會判斷是否滿足 附加條件。 比如我們添加幾條規則: name字段: grade1 , condition字段: {score}<100 name字段: grade2, condition字段: {score}>100 and {score}<200 name 字段: grade3, condition字段 : {score}>200 and {score}<300 這里 {score} 表示 think_members 表 中字段 score的值。 那么這時候 $auth->check('grade1',uid) 是判斷用戶積分是不是0-100 $auth->check('grade2',uid) 判斷用戶積分是不是在100-200 $auth->check('grade3',uid) 判斷用戶積分是不是在200-300 Auth 類認證的使用方法 大致如上,是否有點相見恨晚的感覺? ---------------------------------------------------- 在使用Auth類前需要配置config.php ~~~ 'AUTH_CONFIG'=>array( 'AUTH_ON' => true, //認證開關 'AUTH_TYPE' => 1, // 認證方式,1為時時認證;2為登錄認證。 'AUTH_GROUP' => 'think_auth_group', //用戶組數據表名 'AUTH_GROUP_ACCESS' => 'think_auth_group_access', //用戶組明細表 'AUTH_RULE' => 'think_auth_rule', //權限規則表 'AUTH_USER' => 'think_members'//用戶信息表 ) ~~~ 需要導入數據庫 ~~~ -- ---------------------------- -- think_auth_rule,規則表, -- id:主鍵,name:規則唯一標識, title:規則中文名稱 status 狀態:為1正常,為0禁用,condition:規則表達式,為空表示存在就驗證,不為空表示按照條件驗證 -- ---------------------------- DROP TABLE IF EXISTS `think_auth_rule`; CREATE TABLE `think_auth_rule` ( `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, `name` char(80) NOT NULL DEFAULT '', `title` char(20) NOT NULL DEFAULT '', `status` tinyint(1) NOT NULL DEFAULT '1', `condition` char(100) NOT NULL DEFAULT '', PRIMARY KEY (`id`), UNIQUE KEY `name` (`name`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; -- ---------------------------- -- think_auth_group 用戶組表, -- id:主鍵, title:用戶組中文名稱, rules:用戶組擁有的規則id, 多個規則","隔開,status 狀態:為1正常,為0禁用 -- ---------------------------- DROP TABLE IF EXISTS `think_auth_group`; CREATE TABLE `think_auth_group` ( `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, `title` char(100) NOT NULL DEFAULT '', `status` tinyint(1) NOT NULL DEFAULT '1', `rules` char(80) NOT NULL DEFAULT '', PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; -- ---------------------------- -- think_auth_group_access 用戶組明細表 -- uid:用戶id,group_id:用戶組id -- ---------------------------- DROP TABLE IF EXISTS `think_auth_group_access`; CREATE TABLE `think_auth_group_access` ( `uid` mediumint(8) unsigned NOT NULL, `group_id` mediumint(8) unsigned NOT NULL, UNIQUE KEY `uid_group_id` (`uid`,`group_id`), KEY `uid` (`uid`), KEY `group_id` (`group_id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; ~~~ 最后需要下載Auth類文件到你的項目中,大家可以下載我上傳的附件, Auth類在ThinkPHP代碼倉庫中的位置在: https://github.com/liu21st/extend/blob/master/Extend/Library/ORG/Util/Auth.class.php 【目前Auth認證還沒用示例, 歡迎廣大TPer 貢獻示例!!!】 ## auth demo 本文轉自ThinkPHP官網的[Auth權限認證暴力來襲,有圖有碼有種子,絕對暴力!](http://www.thinkphp.cn/code/714.html) auth的相關介紹說明,官方網站上已經有很多相關文章了,我就不再重復啰嗦了.在這里,我直接上demo以及簡要的說下注意事項及一點點建議.廢話不多說,直接進入主題. 一、auth認證原理 auth類通過認證用戶uid所在的角色組是否擁有對應的權限.如圖所示 ![2015-07-26/55b4a67537019](http://box.kancloud.cn/2015-07-26_55b4a67537019.png) 二、準備工作 1、建表(主要對新增的字段或表作說明,如沒有改變過的表,這里不做說明) 1) tk_auth_group 用戶組 ~~~ CREATE TABLE `tk_auth_group` ( `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, `title` char(100) NOT NULL DEFAULT '', `status` tinyint(1) NOT NULL DEFAULT '1', `rules` char(80) NOT NULL DEFAULT '', `describe` char(50) NOT NULL DEFAULT '', #新增,對用戶組作簡單的說明 PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; ~~~ 2) tk_auth_rule 規則表 ~~~ CREATE TABLE `tk_auth_rule` ( `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, `name` char(80) NOT NULL DEFAULT '', `title` char(20) NOT NULL DEFAULT '', `type` tinyint(1) NOT NULL DEFAULT '1', `status` tinyint(1) NOT NULL DEFAULT '1', `condition` char(100) NOT NULL DEFAULT '', `mid` tinyint(3) unsigned NOT NULL DEFAULT '0', #新增,外鍵,和tk_modules的id對應,對規則分類處理,方便管理 PRIMARY KEY (`id`), UNIQUE KEY `name` (`name`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; ~~~ 3) tk_modules 模塊表 ~~~ CREATE TABLE `tk_modules` ( `id` tinyint(3) unsigned NOT NULL AUTO_INCREMENT, `moduleName` varchar(20) NOT NULL DEFAULT '', #模塊名稱 PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; ~~~ 4) tk_members 用戶表 ~~~ CREATE TABLE `tk_members` ( `uid` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, `username` varchar(20) NOT NULL DEFAULT '', `password` char(32) NOT NULL DEFAULT '', `score` mediumint(8) unsigned NOT NULL, #用戶積分 PRIMARY KEY (`uid`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; ~~~ 2、配置文件 1) 數據庫項配置(本人采用的是pdo方式,如果你的環境不支持pdo,則手動改為其它方式) ~~~ //數據庫配置 //pdo類型,采用dsn方式連接 'DB_TYPE'=>'pdo', 'DB_USER'=>'root', 'DB_PWD'=>'123456', 'DB_PREFIX'=>'tk_', 'DB_PORT'=>'3306', 'DB_DSN'=>'mysql:host=localhost;dbname=auth;charset=utf8', ~~~ 2) 角色組設置,設置超級管理員組,直接跳過認證 ~~~ //超級管理員id,擁有全部權限,只要用戶uid在這個角色組里的,就跳出認證.可以設置多個值,如array('1','2','3') 'ADMINISTRATOR'=>array('1'), ~~~ 3、注意事項 1)運行環境:要求php版本5.3以上 2)對原auth類作了小小的改動.原文件155行左右 ~~~ $user_groups = M() ->table($this->_config['AUTH_GROUP_ACCESS'] . ' a') ->where("a.uid='$uid' and g.status='1'") ->join($this->_config['AUTH_GROUP']." g on a.group_id=g.id") ->field('id,rules')->select(); //原field方法的參數為field('rules'),增加id主要是獲取用戶組的id,便于驗證超級管理員. $groups[$uid]=$user_groups?:array(); ~~~ 建議官方在下次更新能添加id,或增加一個參數,便于用戶靈活設置 三、安裝說明 使用說明請看壓縮包的readme.md文件.另外代碼我都作了詳細的說明,大家一看就懂.下面上幾張暴力圖. ![2015-07-26/55b4a79ea4a6b](http://box.kancloud.cn/2015-07-26_55b4a79ea4a6b.png) ![2015-07-26/55b4a7ac8f57d](http://box.kancloud.cn/2015-07-26_55b4a7ac8f57d.png) ![2015-07-26/55b4a7b74a7ca](http://box.kancloud.cn/2015-07-26_55b4a7b74a7ca.png) ![2015-07-26/55b4a7c534c29](http://box.kancloud.cn/2015-07-26_55b4a7c534c29.png) ![2015-07-26/55b4a7d2d217d](http://box.kancloud.cn/2015-07-26_55b4a7d2d217d.png) 四、結語 本demo提供給大家互相學習,希望通過它對auth認證有一個全面的了解.其實基于tp使用auth權限認證沒有那么復雜,那么難.希望通過能在此基礎上擴展出再多的功能來. 補充: 用戶密碼都是admin 附件 [auth.zip](http://www.thinkphp.cn/code/download/id/714.html)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看