RBAC是Role-Based Access Control的首字母,譯成中文即基于角色的權限訪問控制,說白了也就是用戶通過角色與權限進行關聯[其架構靈感來源于操作系統的GBAC(GROUP-Based Access Control)的權限管理控制]。簡單的來說,一個用戶可以擁有若干角色,每一個角色擁有若干權限。這樣,就構造成“用戶-角色-權限”的授權模型。在這種模型中,用戶與角色之間,角色與權限之間,一般者是多對多的關系。其對應關系如下:
在許多的實際應用中,系統不只是需要用戶完成簡單的注冊,還需要對不同級別的用戶對不同資源的訪問具有不同的操作權限。且在企業開發中,權限管理系統也成了重復開發效率最高的一個模塊之一。而在多套系統中,對應的權限管理只能滿足自身系統的管理需要,無論是在數據庫設計、權限訪問和權限管理機制方式上都可能不同,這種不致性也就存在如下的憋端:
? 維護多套系統,重復造輪子,時間沒用在刀刃上
? 用戶管理、組織機制等數據重復維護,數據的完整性、一致性很難得到保障
? 權限系統設計不同,概念理解不同,及相應技術差異,系統之間集成存在問題,單點登錄難度大,也復雜的企業系統帶來困難
RBAC是基于不斷實踐之后,提出的一個比較成熟的訪問控制方案。實踐表明,采用基于RBAC模型的權限管理系統具有以下優勢:由于角色、權限之間的變化比角色、用戶關系之間的變化相對要慢得多,減小了授權管理的復雜性,降低管理開銷;而且能夠靈活地支持應用系統的安全策略,并對應用系統的變化有很大的伸縮性;在操作上,權限分配直觀、容易理解,便于使用;分級權限適合分層的用戶級形式;重用性強。
ThinkPHP中RBAC實現體系
ThinkPHP中RBAC基于Java的Spring的Acegi安全系統作為參考原型,并做了相應的簡化處理,以適應當前的ThinkPHP結構,提供一個多層、可定制的安全體系來為應用開發提供安全控制。安全體系中主要有以下幾部分:
? 安全攔截器
? 認證管理器
? 決策訪問管理器
? 運行身份管理器
安全攔截器
安全攔截器就好比一道道門,在系統的安全防護系統中可能存在很多不同的安全控制環節,一旦某個環節你未通過安全體系認證,那么安全攔截器就會實施攔截。
認證管理器
防護體系的第一道門就是認證管理器,認證管理器負責決定你是誰,一般它通過驗證你的主體(通常是一個用戶名)和你的憑證(通常是一個密碼),或者更多的資料來做到。更簡單的說,認證管理器驗證你的身份是否在安全防護體系授權范圍之內。
訪問決策管理
雖然通過了認證管理器的身份驗證,但是并不代表你可以在系統里面肆意妄為,因為你還需要通過訪問決策管理這道門。訪問決策管理器對用戶進行授權,通過考慮你的身份認證信息和與受保護資源關聯的安全屬性決定是是否可以進入系統的某個模塊,和進行某項操作。例如,安全規則規定只有主管才允許訪問某個模塊,而你并沒有被授予主管權限,那么安全攔截器會攔截你的訪問操作。
決策訪問管理器不能單獨運行,必須首先依賴認證管理器進行身份確認,因此,在加載訪問決策過濾器的時候已經包含了認證管理器和決策訪問管理器。
為了滿足應用的不同需要,ThinkPHP 在進行訪問決策管理的時候采用兩種模式:登錄模式和即時模式。登錄模式,系統在用戶登錄的時候讀取改用戶所具備的授權信息到 Session,下次不再重新獲取授權信息。也就是說即使管理員對該用戶進行了權限修改,用戶也必須在下次登錄后才能生效。即時模式就是為了解決上面的問題,在每次訪問系統的模塊或者操作時候,進行即使驗證該用戶是否具有該模塊和操作的授權,從更高程度上保障了系統的安全。
運行身份管理器
運行身份管理器的用處在大多數應用系統中是有限的,例如某個操作和模塊需要多個身份的安全需求,運行身份管理器可以用另一個身份替換你目前的身份,從而允許你訪問應用系統內部更深處的受保護對象。這一層安全體系目前的 RBAC 中尚未實現。
ThinkPHP中RBAC認證流程
對應上面的安全體系,ThinkPHP 的 RBAC 認證的過程大致如下:
1、判斷當前模塊的當前操作是否需要認證
2、如果需要認證并且尚未登錄,跳到認證網關,如果已經登錄 執行5
3、通過委托認證進行用戶身份認證
4、獲取用戶的決策訪問列表
5、判斷當前用戶是否具有訪問權限
權限管理的具體實現過程
RBAC相關的數據庫介紹
在ThinkPHP完整包,包含了RBAC處理類RBAC.html' target='_blank'>class.php文件,位于Extend/Library/ORG/Util。打開該文件,其中就包含了使用RBAC必備的4張表,SQL語句如下(復制后請替換表前綴):
CREATE TABLE IF NOT EXISTS `think_access` ( `role_id` smallint(6) unsigned NOT NULL, `node_id` smallint(6) unsigned NOT NULL, `level` tinyint(1) NOT NULL, `module` varchar(50) DEFAULT NULL, KEY `groupId` (`role_id`), KEY `nodeId` (`node_id`)) ENGINE=MyISAM DEFAULT CHARSET=utf8;CREATE TABLE IF NOT EXISTS `think_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, PRIMARY KEY (`id`), KEY `level` (`level`), KEY `pid` (`pid`), KEY `status` (`status`), KEY `name` (`name`)) ENGINE=MyISAM DEFAULT CHARSET=utf8;CREATE TABLE IF NOT EXISTS `think_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, PRIMARY KEY (`id`), KEY `pid` (`pid`), KEY `status` (`status`)) ENGINE=MyISAM DEFAULT CHARSET=utf8 ;CREATE TABLE IF NOT EXISTS `think_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相關的數據庫表及字段作一下介紹:
表名字段名字段類型作用ly_useridINT用戶ID(唯一識別號)usernameVARCHAR(16)用戶名passwordVARCHAR(32)密碼emailVARCHAR(100)用戶郵箱create_timeTIMESTAMP創建時間(時間戳)logintimeTIMESTAMP最近一次登錄時間(時間戳)loginipVARCHAR(15)最近登錄的IP地址statusTINYINT(1)啟用狀態:0:表示禁用;1:表示啟用remarkVARCHAR(255)備注信息ly_roleidINT角色IDnameVARCHAR(20)角色名稱pidSMALLINT(6)父角色對應IDstatusTINYINT(1)啟用狀態(同上)remarkVARCHAR(255)備注信息ly_nodeidSMALLINT(6)節點IDnameVARCHAR(20)節點名稱(英文名,對應應用控制器、應用、方法名)titleVARCHAR(50)節點中文名(方便看懂)statusTINYINT(1)啟用狀態(同上)remarkVARCHAR(255)備注信息sortSMALLINT(6)排序值(默認值為50)pidSMALLINT(6)父節點ID(如:方法pid對應相應的控制器)levelTINYINT(1)節點類型:1:表示應用(模塊);2:表示控制器;3:表示方法ly_role_useruser_idINT用戶IDrole_idSMALLINT(6)角色IDly_accessrole_idSMALLINT(6)角色IDnode_idSMALLINT(6)節點IDlevel module以下是數據庫表各字段的關聯關系:
實現RBAC管理的前導性工作
基于ThinkPHP實現RBAC的權限管理系統中,首先要做一些前導性的工作(系統數據庫設計TP已經為我們完成了),主要分以下幾個方面:
? 用戶(增、刪、改、查)
? 角色(增、刪、改、查)
? 節點(增、刪、改、查)
? 配置權限(更新權限)
具體實現的代碼如下(相關解釋均在注釋之中):
?phpnamespace Home/Controller;use Home/Controller/BaseController;use Home/Model/AdminUserModel;use Org/Util/Tree;use Think/Page;class RbacController extends BaseController //初始化操作 public function _initialize() if (!IS_AJAX) $this- error( 你訪問的頁面不存在,請稍后再試 public function userIndex() if (IS_POST) { $condition[ username ] = array( like , % . trim(I( keybord )) . % $model = D( AdminUser $count = $model- where($condition)- count(); $Page = new Page($count, 3); $Page- setConfig( theme , %FIRST% %UP_PAGE% %LINK_PAGE% %DOWN_PAGE% %END% $show = $Page- show(); //select search $list = $model- where($condition)- field( password , true)- relation(true)- limit($Page- firstRow . , . $Page- listRows)- select(); $this- show = $show; $this- list = $list; $this- display( AdminUser/index } else { $model = D( AdminUser $count = $model- count(); $Page = new Page($count, 6); $Page- setConfig( header , 共%TOTAL_ROW%條 $Page- setConfig( first , 首頁 $Page- setConfig( last , 共%TOTAL_PAGE%頁 $Page- setConfig( prev , 上一頁 $Page- setConfig( next , 下一頁 $Page- setConfig( link , indexpagenumb $Page- setConfig( theme , %FIRST% %UP_PAGE% %LINK_PAGE% %DOWN_PAGE% %END% $show = $Page- show(); //select search $list = $model- field( password , true)- relation(true)- limit($Page- firstRow . , . $Page- listRows)- select(); $this- show = $show; $this- list = $list; $this- display( Rbac/userIndex * 平臺用戶異步驗證 public function checkUser() $username = trim(I( username $conditions = array( username = :username $result = M( AdminUser )- where($conditions)- bind( :username , $username)- find(); //如果不存在,則可以創建,也就是返回的是true if (!$result) { echo true } else { echo false exit(); * 創建平臺用戶,這里開啟了服務器驗證 public function createAdminUser() if (IS_POST) { * [實例化User對象] * D方法實例化模型類的時候通常是實例化某個具體的模型類,如果你僅僅是對數據表進行基本的CURD操作的話, * 使用M方法實例化的話,由于不需要加載具體的模型類,所以性能會更高。 $model = D( AdminUser * 如果創建失敗 表示驗證沒有通過 輸出錯誤提示信息 * $model- create() 會自動調用驗證規則 if (!$model- create()) return $this- error($model- getError()); //$username = $model- username; //$model- add() 插入數據后會自動的摧毀數據 if (!$uid = $model- add()) return $this- success( 注冊失敗 , U( Rbac/userIndex //獲取用戶id // 如果是注冊用戶的話// session( uid , $uid);// session( username , $username); //用戶添加成功后,給用戶角色表添加數據 $role[ role_id ] = I( role_id $role[ user_id ] = $uid; //添加該管理員操作到操作日志中 $desc = 給ID為:[ . $_POST[ role_id ] . ]的角色,新增用戶:[ . $_POST[ username ] . ],密碼為:[ . $_POST[ password ] . ] . 其他參數 . $_POST; addOperationLog($desc); if (D( AdminRoleUser )- add($role)) { return $this- success( 添加平臺用戶成功 , U( Rbac/userIndex } else { return $this- error( 添加平臺用戶失敗 , U( Rbac/userIndex return $this- success( 添加平臺用戶成功 , U( Rbac/userIndex $this- role_list = M( AdminRole )- select(); $this- display( Rbac/createAdminUser * 改變用戶角色 * 可以支持不執行SQL而只是返回SQL語句:$User- fetchSql(true)- add($data); public function updateUser() $userId = I( get.id if (IS_POST) { $data[ user_id ] = I( post.user_id $data[ role_id ] = I( post.role_id $model = M( AdminRoleUser if ($model- where(array( user_id = $data[ user_id ]))- delete() == false) { return $this- error( 用戶角色修改失敗 , U( Rbac/updateUser , array( id = $userId))); if ($model- add($data) == false) { return $this- error( 用戶角色修改失敗 , U( Rbac/updateUser , array( id = $userId))); return $this- success( 用戶角色修改成功 , U( Rbac/userIndex $this- role_list = M( AdminRole )- select(); $this- user = M( AdminUser )- join( tour_admin_role_user ON tour_admin_role_user.user_id = tour_admin_user.id )- where(array( id = $userId))- field( user_id,username,role_id )- find(); $this- display(); //刪除用戶 public function delUser() $user_id = I( post.id , , int $user = D( AdminUser $result = $user- relation(true)- where(array( id = $user_id))- delete(); if ($result) { //添加該管理員操作到操作日志中 $desc = 刪除用戶ID: . $user_id . 成功 addOperationLog($desc); $response = [ status = 200, errmsg = 刪除成功 , dataList = $result]; return $this- ajaxReturn($response, JSON //添加該管理員操作到操作日志中 $desc = 刪除用戶ID: . $user_id . 失敗 addOperationLog($desc); $response = [ status = 500, errmsg = 刪除失敗 , dataList = $result]; return $this- ajaxReturn($response, JSON //設置用戶狀態 public function userStatus() $uid = I( post.id $db = M( AdminUser $status = $db- where(array( id = $uid))- getField( status $status = ($status == 1) ? 0 : 1; if ($db- where(array( id = $uid))- setField( status , $status)) { $response = [ status = 200, errmsg = 改變成功 , dataList = $status]; return $this- ajaxReturn($response, JSON //添加該管理員操作到操作日志中 $desc = 設置用戶狀態: . $uid . 失敗 addOperationLog($desc); $response = [ status = 500, errmsg = 改變失敗 , dataList = $status]; return $this- ajaxReturn($response, JSON /***********************************節點開始****************************************************/ public function nodeIndex() $db = M( AdminNode $node = $db- order( id )- select(); $this- nodelist = Tree::create($node); $this- display( Rbac/nodeIndex //創建權限表單處理 public function createNode() $db = M( AdminNode //創建權限表單處理 if (IS_POST) { $db- create(); if (!$db- add()) { return $this- error( 權限添加失敗 , U( Rbac/nodeIndex return $this- success( 權限添加成功 , U( Rbac/nodeIndex $node = $db- where( level !=3 )- order( sort )- select(); $this- nodelist = Tree::create($node); $this- display(); * 刪除權限 public function delNode() $result = M( AdminNode )- where(array( id = I( post.id , , int )))- delete(); if ($result) { $response = [ status = 200, errmsg = 刪除成功 , dataList = $result]; return $this- ajaxReturn($response, JSON $response = [ status = 500, errmsg = 刪除失敗 , dataList = $result]; return $this- ajaxReturn($response, JSON * 設置權限狀態 public function NodeStatus() $id = I( post.id $db = M( AdminNode $status = $db- where(array( id = $id))- getField( status $status = ($status == 1) ? 0 : 1; if ($db- where(array( id = $id))- setField( status , $status)) { $response = [ status = 200, errmsg = 修改成功 , dataList = $status]; return $this- ajaxReturn($response, JSON $response = [ status = 500, errmsg = 修改失敗 , dataList = $status]; return $this- ajaxReturn($response, JSON * 該節點是否在菜單欄顯示 public function showMenus() $id = I( post.id $db = M( AdminNode $show = $db- where(array( id = $id))- getField( menus $menus = ($show == 1) ? 0 : 1; $result = $db- where(array( id = $id))- setField( menus , $menus); if ($result) { $response = [ status = 200, errmsg = 修改成功 , dataList = $result]; return $this- ajaxReturn($response, JSON $response = [ status = 500, errmsg = 修改失敗 , dataList = $result]; return $this- ajaxReturn($response, JSON /***********************************角色開始****************************************************/ public function roleIndex() $db = M( AdminRole $this- rolelist = $db- select(); $this- display(); *創建角色 public function createAdminRole() if (IS_POST) { $name = I( post.name , , strip_tags $remark = I( post.remark , , strip_tags $pid = I( post.pid , , strip_tags // 用strip_tags過濾$_GET[ title ] if (empty($name)) return $this- error( 角色名稱不能為空 $role = M( AdminRole $where[ name ] = :name $roleName = $role- where($where)- bind( :name , $name, /PDO::PARAM_STR)- getField( name if ($roleName) return $this- error( 角色名稱: . $name . 已經存在 , U( Rbac/roleIndex $role- name = $name; $role- remark = $remark; $role- pid = $pid; //create方法并不算是連貫操作,因為其返回值可能是布爾值,所以必須要進行嚴格判斷。 if ($role- create()) { // 如果主鍵是自動增長型 成功后返回值就是最新插入的值 $result = $role- field( name,remark,pid )- add(); //如果在add方法之前調用field方法,則表示只允許寫入指定的字段數據,其他非法字段將會被過濾 if (!$result) return $this- error( 角色添加失敗 , U( Rbac/createpartent return $this- success( 角色添加成功 , U( Rbac/roleIndex return $this- success( 角色添加成功 , U( Rbac/roleIndex $this- display(); *添加權限Node位權限表,Access為權限-角色關聯表 public function addNode() $rid = I( rid , , int if (!is_numeric($rid)) return $this- success( 參數類型錯誤,必須是數字 , U( Rbac/roleIndex //getFieldById針對某個字段(ID)查詢并返回某個字段(name)的值 $roleModel = M( AdminRole $roleWhere[ id ] = :id $role_name = $roleModel- where($roleWhere)- bind( :id , $rid, /PDO::PARAM_INT)- getField( name if ($role_name == false) return $this- success( 沒有找到該角色 , U( Rbac/roleIndex //根據角色遍歷所有權限 $access = M( AdminAccess if (IS_POST) { $actions = I( post.actions try { $access- startTrans(); $where[ role_id ] = :role_id $mod1 = $access- where($where)- bind( :role_id , $rid)- delete(); if (!$mod1) $access- rollback(); $data = array(); foreach ($actions as $value) { $tmp = explode( _ , $value); $data[] = array( role_id = $rid, node_id = $tmp[0], level = $tmp[1] if (!($access- addAll($data))) { $access- rollback(); } else { $access- commit(); return $this- success( 權限設置成功 , U( Rbac/addNode , array( rid = $rid))); } catch (/Exception $e) { $access- rollback(); return $this- success( 權限設置異常 , U( Rbac/addNode , array( rid = $rid))); $node = M( AdminNode )- order( id )- select(); $node_list = Tree::create($node); $node_arr = array(); foreach ($node_list as $value) { $conditions[ node_id ] = $value[ id $conditions[ role_id ] = $rid; $count = $access- where($conditions)- count(); if ($count) { $value[ access ] = 1 } else { $value[ access ] = 0 $node_arr[] = $value; $this- role_name = $role_name; $this- node_list = $node_arr; $this- rid = $rid; $this- display(); *刪除角色以及角色所擁有的權限 public function delRole() $role_id = I( post.role_id , , int $user = D( AdminRole $result = $user- relation(true)- where(array( id = $role_id))- delete(); if ($result) { $response = [ status = 200, errmsg = 修改成功 , dataList = $result]; return $this- ajaxReturn($response, JSON $response = [ status = 500, errmsg = 修改失敗 , dataList = $result]; return $this- ajaxReturn($response, JSON * 設置角色狀態 public function roleStatus() $rid = I( post.rid $db = M( AdminRole $status = $db- where(array( id = $rid))- getField( status $status = ($status == 1) ? 0 : 1; if ($db- where(array( id = $rid))- setField( status , $status)) { $response = [ status = 200, errmsg = 修改成功 , dataList = $status]; return $this- ajaxReturn($response, JSON $response = [ status = 500, errmsg = 修改失敗 , dataList = $status]; return $this- ajaxReturn($response, JSON }
ThinkPHP中RBAC類的詳解
在ThinkPHP處理權限管理中,真正的難點并不是在RBAC類的使用上,上面相關的處理(權限配置,節點管理等);所以當完成以上工作,其實RBAC系統已經完成了90%。下面先從ThinkPHP中RBAC的配置說起(詳細請參看對應的注釋內容):
?php
USER_AUTH_ON = true, //是否開啟權限驗證(必配) USER_AUTH_TYPE = 1, //驗證方式(1、登錄驗證;2、實時驗證) USER_AUTH_KEY = uid , //用戶認證識別號(必配) ADMIN_AUTH_KEY = superadmin , //超級管理員識別號(必配) USER_AUTH_MODEL = user , //驗證用戶表模型 ly_user USER_AUTH_GATEWAY = /Public/login , //用戶認證失敗,跳轉URL AUTH_PWD_ENCODER = md5 , //默認密碼加密方式 RBAC_SUPERADMIN = admin , //超級管理員名稱
NOT_AUTH_MODULE = Index,Public , //無需認證的控制器 NOT_AUTH_ACTION = index , //無需認證的方法 REQUIRE_AUTH_MODULE = , //默認需要認證的模塊 REQUIRE_AUTH_ACTION = , //默認需要認證的動作 GUEST_AUTH_ON = false, //是否開啟游客授權訪問 GUEST_AUTH_ID = 0, //游客標記 RBAC_ROLE_TABLE = ly_role , //角色表名稱(必配) RBAC_USER_TABLE = ly_role_user , //用戶角色中間表名稱(必配) RBAC_ACCESS_TABLE = ly_access , //權限表名稱(必配) RBAC_NODE_TABLE = ly_node , //節點表名稱(必配));
注意:
? 以上有的配置項并非必須的,但標有“必配”,則必須配置
? 無需認證的權限和方法與需要認證的模塊和動作可以根據需要選擇性配置,還有部分權限相關配置并未列表出
RBAC處理類提供靜態的方法
ThinkPHP的RBAC處理類提供給我們很多相關的靜態方法如下:
方法名接收參數說明返回值說明RBAC::authenticate(map,map,model=$map:ThinkPHP數據庫處理類的where條件參
數$model:用戶表名,默認取配置文件C( USER_AUTH_MODEL )
arrayRBAC::authenticate(array( username = admin , userpwd = I( POST.pwd , , md5 )));
返回值是在用戶表中,以$map為條件where的查閱結果集
0RBAC::saveAccessList($authId=null);$authId:用戶識別號,默認取C( USER_AUTH_KEY 返回一個空值如果驗證方式為登錄驗證,則將權限寫入session中,否則不作任何處理RBAC::getRecordAccessList(authId=null,authId=null,module=$authId:用戶識別號(可不傳)
$module:當前操作的模塊名稱
Array返回一個包含權限的ID的數組RBAC::checkAccess()無返回true或false檢查當前操作是否需要認證(根據配置中需要認證和不需要評論的模塊或方法得出)RBAC::checkLogin()無true如果當前操作需要認證且用戶沒有登錄,繼續檢測是否開啟游客授權。如果開啟游客授權,則寫入游客權限;否則跳到登錄頁RBAC::AccessDecision($appName=APP_NAME)$appName:選傳,有默認值true:表示有操作權限
false:無操作權限
AccessDecision(appName=APPNAME)方法,檢測當前項目模塊操作,是否存在于appName=APPNAME)方法,檢測當前項目模塊操作,是否存在于_SESSION[ _ACCESS_LIST ]數組中$_SESSION[ _ACCESS_LIST ][ 當前操作 ][ 當前模塊 ][ 當前操作 ]是否存在。如果存在表示有權限,返回true;否則返回flase。RBAC::getAccessList($authId)$authId:用戶識別號(選傳,程序自動獲?。〢rray通過數據庫查詢取得當前認證號的所有權限列表RBAC::getModuleAccessList(authId,authId,module)$authId:用戶識別號(必)$module:對應的模塊(必)Array返回指定用戶可訪問的節點權限數組注意:在使用RBAC::AccessDecision()方法時,如果你開啟了項目分組,則必須傳入當前分組,代碼如下:
//權限驗證if(C( USER_AUTH_ON ) !$notAuth) {import( ORG.Util.RBAC //使用了項目分組,則必須引入GROUP_NAMERBAC::AccessDecision(GROUP_NAME) || $this- error( 你沒有對應的權限 }RBAC處理類的實際應用
在完成用戶登錄,角色創建,節點增刪改查的工作后,就只剩下了RBAC如何在對應程序代碼中應用了。挻簡單的,只用在原來的代碼其他上改動幾個地方即可。
? 用戶登錄時,寫入用戶權限
? 用戶操作時,進行權限驗證
下面是用戶登錄時的實現代碼:
?phpnamespace Home/Controller;use Think/Controller;use Org/Util/Rbac;class LoginController extends Controller public function index() $this- display(); * 異步驗證賬號 public function checkUser() $username = I( username $conditions = array( username = :username $result = M( User )- where($conditions)- bind( :username , $username)- find(); //如果不存在,則可以創建,也就是返回的是true if (!$result) { echo false } else { echo true exit(); * 異步驗證密碼 public function checkPwd() $username = I( post.username $password = I( post.password $conditions = array( username = :username $result = M( User )- where($conditions)- bind( :username , $username)- find(); if (md5($password) != $result[ password ]) { echo false } else { echo true exit(); * 檢查登錄 public function checkLogin() if (!IS_POST) $this- error( 非法訪問 // 采用htmlspecialchars方法對$_GET[ name ] 進行過濾,如果不存在則返回空字符串 $username = I( post.username , , htmlspecialchars // 采用正則表達式進行變量過濾,如果正則匹配不通過的話,則返回默認值。 //I( get.name , , /^[A-Za-z]+$/ $password = md5(I( post.password $user = D( AdminUser $where = array( username = $username); $fields = array( id , password , username , status , expire , logintime // 之查找需要的字段 $result = $user- where($where)- field($fields)- find(); if (!$result || $password != $result[ password ]) return $this- error( 賬號或密碼錯誤 ,U( Home/Login/index if ($result[ status ] == 0) return $this- error( 該用戶被鎖定,暫時不可登錄 ,U( Home/Login/index // 是否記住我的登錄,設置一個Cookie,寫在客戶端 if (isset($_POST[ remember ])) { $value = $result[ id ] . | . get_client_ip() . | . $result[ username $value = encrytion($value, 1); @setcookie( remember , $value, C( AUTO_LOGIN_LIFETIME ), / // 每天登錄增加經驗值 $today = strtotime(date( Y-m-d // 獲取今天0時0分0秒的時間 // 如果上次的登錄時間小于今天的時間,則增加經驗值 $where2 = array( id = $result[ id if ($result[ logintime ] $today) { $user- where($where2)- setInc( expire , 10); //更新登錄戶登錄信息 $data_arr = array( id = $result[ id ], logintime = time(), loginip = get_client_ip(), if ($user- save($data_arr)) { // 獲取$_SESSION[ user_id ] 如果不存在則默認為0 session( uid , 0); session( username , $result[ username session( loginAccount , $result[ username session( loginUserName , $result[ username session( uid , $result[ id //RBAC 開始,用戶認證SESSION標記 ,默認為 authId session(C( USER_AUTH_KEY ), $result[ id //如果為超級管理員,則無需驗證 if ($_SESSION[ username ] == C( RBAC_SUPERADMIN )) session(C( ADMIN_AUTH_KEY ), true); //用于檢測用戶權限的方法,并保存到Session中,讀取用戶權限 Rbac::saveAccessList($result[ id //添加操作日志中 $desc = 登陸成功 addOperationLog($desc); return $this- redirect( Index/index } else { return $this- error( 2222222222222 public function memberInfo() $user_id = session( user_id $user = D( User $where = array( user_id = $user_id); $result = $user- where($where)- select(); $this- result = $result; $this- display(); * 獲取apikey_values public function apikey() $secret = 6JNVkTk4jHsgF0e1oOVLwOZDeq83pDXa $user_id = I( user_id , , int); // $where = array( user_id = %d ,array($user_id)); $user = M( User // $result = $user- where($where)- find(); $result = $user- where( user_id = %d , array($user_id))- find(); $hash = sha1($result[ user_id ] . $result[ password ] . $secret); $data = array( apikey_value = $hash, apikey_time = time() $where = array( user_id = $user_id); $user- where($where)- save($data); $this- ajaxReturn($hash); public function hash() if (!IS_POST) { $out_data = array( err_msg = request method is error. , is_success = Fail exit(json_encode($out_data)); $username = I( username $password = I( password $where = array( username = $username); $user = M( User $result = $user- where($where)- find(); if (!$result || $password != $result[ password ]) { $out_data = array( err_msg = Username or password is incorrect. , is_success = Fail exit(json_encode($out_data)); * HASH生成規則 $secret = 6JNVkTk4jHsgF0e1oOVLwOZDeq83pDXa $hash = sha1($result[ user_id ] . $result[ password ] . $secret); $where = array( user_id = $result[ user_id $data[ apikey_value ] = $hash; $data[ apikey_time ] = time(); $user- where($where)- save($data); $out_data = array( is_success = Success , hash = $hash exit(json_encode($out_data)); public function relationTest() echo get_client_ip(); public function error1(){ $this- display(); public function logout() session( username , NULL); session_unset(); session_destroy(); return $this- redirect( Login/index }
接著在控制器目錄創建一個CommonAction.class.php文件,然后改寫所有要權限驗證的類,讓其繼承自CommonAction。代碼如下:
?phpnamespace Home/Controller;use Think/Controller;use Org/Util/Rbac;class BaseController extends Controller// /**// * ThikPHP自動運行方法,每一次,都會自動運行這個方法// * 要判斷一個session值是否已經設置,可以使用// session( ?name 相當于:isset($_SESSION[ name // */ public function _initialize(){ /***************************************網站開關****************************************************/ if(!C( WEB_STATE )) exit( 網站維護中 /***********************************沒有登錄時候時需要執行的代碼**************************************/ if(!isset($_SESSION[C( USER_AUTH_KEY )])) return $this- redirect( Login/index /***************************************自動登錄配置************************************************/ if(isset($_COOKIE[ remember ]) !isset($_SESSION[ uid ])){ // Cookie 解密 $value = encrytion($_COOKIE[ remember $result = explode( | ,$value); // 判斷IP地址是否一樣 if($result[1] == get_client_ip()){ session( uid ,$result[0]); session( uid ,$result[2]); /***************************************權限認證****************************************************/ $Public = in_array(MODULE_NAME,explode( , ,C( NOT_AUTH_MODULE ))) || in_array(ACTION_NAME,explode( , ,C( NOT_AUTH_ACTION ))); // 如果不在公共模塊之中,同時開啟權限驗證的話,則開始認證過程 if(C( USER_AUTH_ON ) !$Public) if(!Rbac::AccessDecision()) //通過accessDecision獲取權限信息,true:表示有操作權限,false:無操作權限 return $this- error( 你沒有對應的權限 //沒有獲取到權限信息時需要執行的代碼 /***************************************導航欄菜單顯示****************************************************/ * 思路: * 1.取出所有權限節點。 * 2.取出當前登錄用戶擁有的模塊權限(取英文名稱)和操作權限(取ID) * 3.對所有權限進行遍歷,先匹配模塊權限,不存在刪除,存在則匹配操作權限 // 超級管理員登錄 if(session(C( ADMIN_AUTH_KEY ))) $menus = D( AdminNode )- where( level = 2 )- relation(true)- order( sort desc )- select();//取出所有節點 }else{ * [1]取出所有的權限,是通過關聯模型從數據庫中獲取的 $menus = D( AdminNode )- where( level = 2 )- relation(true)- order( sort desc )- select(); $module = //存放擁有的模塊 $node_id = //存放擁有的模塊 * [2]獲取當前用戶的所有權限,這個是從RBAC中獲取的 $access_list = $_SESSION[ _ACCESS_LIST //當前用戶所擁有的權限 foreach ($access_list as $key = $value) { foreach ($value as $key1 = $value1) { $module = $module. , .$key1; //字符串拼接模塊名稱 foreach ($value1 as $key2 = $value2) { $node_id = $node_id. , .$value2; //字符串拼操作id * [3]去除沒有權限的節點,通過所有權限和用戶已經擁有的權限比較 foreach ($menus as $key = $value) { $all_node[] = $value[ name if(!in_array(strtoupper($value[ name ]), explode( , , $module))){ unset($menus[$key]); //刪除模塊 }else{ //模塊存在,比較里面的操作 foreach ($value[ node ] as $key1 = $value1) { if(!in_array($value1[ id ], explode( , , $node_id))){ unset($menus[$key][ node ][$key1]); // 刪除操作 $this- menus = $menus;}
在ThinkPHP中提供了一個_initialize()方法,是在類初始化就會執行的,也就是只要后面控制器繼承自CommonAction類,就會在作對應操作時,執行_initialize()方法。
以上就是ThinkPHP中RBAC權限帶菜單欄顯示和詳細權限操作的詳細內容,PHP教程
鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯系我們修改或刪除,多謝。
新聞熱點
疑難解答