上文http://www.BkJia.com/kf/201205/129972.html給大家講解了使用循環輸出九九乘法表,邏輯上還是相對簡單一些,重在給大家提供一種看程序,解析代碼的方法和思路,有什麼意見或者建議可以跟帖批斗....
好了,不多說了,本文來給大家介紹一下“基於角色的訪問控制 ”,
說到權限,大家就很頭疼,怎麼樣能靈活把控好一個用戶的權限,
有些同學會在用戶表中加字段或者是在角色表中加相應的權限字段,
這樣會有一個問題,做起權限來會感覺特別的蹩腳,而且很不靈活,每增加一種權限就要在數據庫中增加一個字段,很不利於項目的迭代開發
那麼我們就需要一種非常靈活的設計模式RBAC,即基於角色的訪問控制;
我來給大家說下這種設計思想:
首先,我們的需求是判斷某一個用戶對當前操作的控制器或控制器的方法是否有權限訪問,
如果多個用戶同時擁有同樣的權限,那我們就需要給這些用戶指定同一個用戶角色,然後只需要通過角色來對操作的訪問進行權限控制,
那我們表結構需要這樣來設計,這個很重要,如下:
第一張數據表(用戶表):
第二張數據表(角色表):
第三張數據表(節點表):
我們使用第三范式來設計關聯表,這樣做的好處是,避免數據冗余,並且對於一對多,多對一的關系都可以清晰的記錄,條理清晰
第四張數據表(節點對應角色表):
第五張數據表(用戶對應角色表):
通過這五張表就可以對權限進行訪問控制,它的具體操作步驟如下:
用戶輸入用戶名密碼登錄,
通過用戶表判斷,如果輸入的用戶名密碼不合法,跳回重新登錄
如果合法,在用戶表中返回用戶的ID號,
通過此用戶ID號,到用戶與角色的關聯表中查詢出用戶的角色ID號,
拿到角色ID號,通過此ID號到角色與節點的關聯表中查詢出此角色擁有的節點訪問權限,
將此權限節點全部存入SESSION中,當用戶訪問某一個模塊的時候,
例如:http://www.lampbroher.net/index.php/stu/index
我們用session中的權限與$_GET['m']與$_GET['a']去對比,
如果$_GET['m']或者$_GET['a']在SESSION中不存在,說明該用戶沒有此權限,作出處理即可。
參考代碼:
RBAC類文件:
<?php
/*+---------------------------------------------------------------------------------------+
| RBAC權限控制類
class Rbac{
private $node_tablename; //定義私有屬性節點表名稱
private $group_auth_tablename; //定義私有屬性組權限表名稱
private $group_tablename; //定義私有屬性用戶組表名稱
private $group_user_tablename; //定義私有屬性用戶歸屬組表名稱
private $user_tablename; //定義私有屬性用戶表名稱
/*
構造方法
@param1 string 節點表名稱
@param2 string 用戶權限表名稱
@param3 string 用戶組表名稱
@param4 string 用戶歸屬組表名稱
@param5 string 用戶表名稱
*/
public function __construct($node_tablename='node',$group_auth_tablename='group_auth',$group_tablename='group',$group_user_tablename='group_member',$user_tablename='member'){
$this->node_tablename = $node_tablename; //獲取節點表名稱
$this->group_auth_tablename = $group_auth_tablename; //獲取用戶權限表名稱
$this->group_tablename = $group_tablename; //獲取用戶組表名稱
$this->group_user_tablename = $group_user_tablename; //獲取用戶歸屬組表名稱
$this->user_tablename = $user_tablename; //獲取用戶表名稱
}
/*
設置節點方法
@param1 string 節點名稱
@param2 string 節點父ID
@param2 string 節點中文說明
@return int 插入節點記錄成功以後的ID
*/
public function set_node($name,$pid,$zh_name=''){
if(!empty($name) && !empty($pid)){
$node = D($this->node_tablename)->insert(array("name"=>$name,"pid"=>$pid,"zh_name"=>$zh_name));
}
return $node;
}
/*
設置權限方法
@param1 int 組ID
@param2 int 節點ID
@return int 插入權限記錄成功以後的ID
*/
public function set_auth($gid,$nid){
if(!empty($gid) && !empty($nid)){
$auth = D($this->group_auth_tablename)->insert(array("gid"=>$gid,"nid"=>$nid));
}
return $auth;
}
/*
獲取節點方法
@param1 int 節點ID
@return array 獲取到節點表的相關信息
*/
public function get_node($id){
if(!empty($id)){
$data = D($this->node_tablename)->field("id,name,pid")->where(array('id'=>$id))->find();
return $data;
}else{
return false;
}
}
/*
獲取組權限方法
@param1 int 用戶組ID
@return array 獲取到組權限表的相關信息
*/
public function get_auth($gid){
if(!empty($gid)){
$data = D($this->group_auth_tablename)->field("nid")->where(array('gid'=>$gid))->select();
return $data;
}else{
return false;
}
}
/*
獲取用戶組方法
@param1 int 用戶ID
@return array 獲取該用戶所對應的用戶組id
*/
public function get_group($uid){
if(!empty($uid)){
$data = D($this->group_user_tablename)->field("gid")->where(array('uid'=>$uid))->select();
return $data;
}else{
return false;
}
}
/*
獲取節點的子節點方法
@param1 int 節點ID
@return array 獲取該節點所對應的全部子節點
*/
public function get_cnode($nid){
if(!empty($nid)){
$cnode = D($this->node_tablename)->field("name")->where(array('pid'=>$nid))->select();
return $cnode;
}else{
return false;
}
}
/*
獲取權限方法
@param1 int 用戶ID
@return array 得到權限列表
*/
public function get_access($uid){
if(!empty($uid)){
//調用獲取組信息方法
$group = $this->get_group($uid);
//遍歷組信息
foreach($group as $v){
//將組ID傳入獲取權限的方法
$auth = $this->get_auth($v['gid']); //獲取該組的權限
}
//遍歷該組的權限數組
foreach($auth as $val){
//將節點的ID傳入獲取節點信息方法
$node[] = $this->get_node($val['nid']); //獲取節點的相關信息
}
//遍歷節點數組,並拼裝
foreach($node as $nval){
if($nval['pid']==0){
$fnode[] = $nval; //將控制器壓入fnode數組
//$cnode = $this->get_cnode($nval['id']);
}else{
$cnode[] = $nval; //將控制器的方法壓入cnode數組
}
}
//將控制器數組和控制器數組拼裝成一個數組
foreach($fnode as $fval){
foreach($cnode as $cval){
if($cval['pid'] == $fval['id']){
$access[$fval['name']][] = $cval['name'];
}
}
}
//返回權限列表數組
return $access;
}else{
return false;
}
}
/*
檢測權限方法
@param1 int 用戶ID
@return boolean 權限禁止與否
*/
public function check($uid){
if(!empty($uid)){
//將權限存入到$_SESSION['Access_List']中
$_SESSION['Access_List'] = $this->get_access($uid);
if(!empty($_GET['m'])){
//判斷此控制器是否被允許
if(array_key_exists($_GET['m'],$_SESSION['Access_List'])){
//判斷此控制器的方法是否被允許
if(in_array($_GET['a'],$_SESSION['Access_List'][$_GET['m']])){
//允許的話返回真
return true;
}else{
//否則返回假
return false;
}
}else{
return false;
}
}else{
return false;
}
}else{
//$_SESSION['user_'.$uid]['Access_List'] = 0;
return false;
}
}
public function show_node(){
$path = APP_PATH.'/controls/';
$handle = opendir($path);
while(false!==($data = readdir($handle))){
if(is_file($path.$data) && $data!='common.class.php' && $data!='pub.class.php'){
$controller = str_replace(".class.php",'',$data);
$res = fopen($path.$data,'r');
$str = fread($res,filesize($path.$data));
$pattern = '/function(.*)\(\)/iU';
preg_match_all($pattern, $str, $matches);
foreach($matches[1] as $v){
$v = trim($v);
$arr[$controller][] = $v;
}
}
}
closedir($handle);
return $arr;
}
}
初始化類:
<?php
/*+---------------------------------------------------------------------------------------+
| 初始化控制器
class Common extends Action {
/*
初始化方法
*/
public function init(){
//如果SESSION為空,則跳轉
if(empty($_SESSION['user_login'])){
$this->redirect("pub/index");
}
$a = new rbac();
if(!$a->check($_SESSION['user_info']['id'])){
echo "<script>alert('您沒有此權限!')</script>";
exit("<script>document.write('<span style=\'font-size:40px;font-weight:bold\'>Access Forbidden');alert('您沒有此權限!');</script>");
$this->redirect("pub/index");
}
}
}
這裡給大家寫了一個簡單的RBAC類,僅供大家學習參考此思想,如有問題可以跟帖回復....
作者 zdrjlamp