RBAC權限管理及使用原生PHP實現


  關於RBAC的原理講解在網上可以找到很多,推薦:編程浪子的RBAC講解,本篇博客就不再累述RBAC的原理到底是什么樣的.

   傳統的權限控制有ACL和RBAC方式,ACL的耦合度很高,擴展性不佳,RBAC很好的解耦合,將權限控制的整個過程涉及的數據表大致分為5張表格:

  1. user表
  2. role表
  3. access表(存儲資源數據)
  4. user_role表
  5. role_access表 

  下面是使用原生PHP來實現RBAC權限控制,是一個很簡單的例子,因為重在理解這個原理,所以要修改的地方有很多,但是,如果跟着代碼一步一步來,那你肯定對RBAC有更深的理解,同時也會發現很多問題,比如多對多的數據表該怎么設計。。。。。

  話不多說,先看一下代碼吧,代碼已經提交到github:https://github.com/searchingbeyond/RBAC-IN-PHP

  先看一下目錄結構:

  

 

然后我會按照便於理解原理的順序來展示代碼:

 /RBAC/backend/index.php     后台管理的主頁面

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
</head>
<body>
	<div>
		<table>
			<tr>
				<td><a href="./UserList.php">用戶列表</a></td>
				<td><a href="./RoleList.php">角色管理</a></td>
				<td><a href="./AccessList.php">權限管理</a></td>
			</tr>
			<tr>
				<td><a href="./AddUser.php">添加用戶</a></td>
				<td><a href="./AddRole.php">添加角色</a></td>
				<td><a href="./AddAccess.php">增加資源</a></td>
			</tr>
		</table>
	</div>
</body>
</html>

  

/RBAC/backend/AddUser.php    添加用戶

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>創建用戶</title>
</head>
<body>
	<div>
		<table>
			<form action="" method="post">
			<caption>用戶信息</caption>
			<tr>
				<td>用戶ID:</td>
				<td><input type="text" name="user_id" style="width:400px"></td>
			</tr>
			<tr>
				<td>用戶名:</td>
				<td><input type="text" name="user_name" style="width:400px"></td>
			</tr>
			<tr>
				<td colspan="2">
					<input type="submit" name="adduser" value="添加">
					<input type="button" value="返回首頁" onclick="location.href='./index.php'">
				</td>
			</tr>
			</form>
		</table>
	</div>
</body>
</html>

<?php 
	if( isset($_POST['adduser']) ){
		$user_id = $_POST['user_id'];
		$user_name = $_POST['user_name'];
		$pdo = new PDO("mysql:host=127.0.0.1;dbname=rbac","root","123456");
		$stmt = $pdo->prepare("insert into user (user_id,user_name) values ( :user_id, :user_name )");
		$stmt->execute(array(":user_id"=>$user_id,":user_name"=>$user_name));
	}
?>

 

/RBAC/backend/UserList.php     顯示用戶列表

<?php 
	$pdo = new PDO("mysql:host=127.0.0.1;dbname=rbac","root","123456");
	$stmt = $pdo->prepare("select * from user");
	$stmt->execute();
	$user_arr = $stmt->fetchAll(PDO::FETCH_ASSOC);
 ?>

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>用戶管理</title>
</head>
<body>
	<div>
		<table>
			<caption>用戶列表</caption>
			<tr>
				<td>用戶名</td>
				<td>狀態</td>
				<td>操作</td>
			</tr>
			<?php if( count($user_arr) ): ?>
				<?php foreach($user_arr as $user): ?>
					 <tr>
					 	<td><?php echo $user['user_name']; ?></td>
					 	<td><?php echo $user['user_status']?"正常":"禁用"; ?></td>
					 	<td>
					 		<a href="EditUser.php?user_id=<?php echo $user['user_id']?>">角色設置</a>
					 	</td>
					 </tr>
				<?php endforeach; ?>
			<?php endif; ?>
		</table>
	</div>
</body>
</html>

  

/RBAC/backend/EditUser.php      用於設置用戶的角色

<?php 
	if( isset($_GET['user_id']) ){
		$pdo = new PDO("mysql:host=127.0.0.1;dbname=rbac","root","123456");

		//查詢用戶信息
		$stmt = $pdo->prepare("select * from user where user_id = :user_id");
		$stmt->execute(array("user_id" => $_GET['user_id']));
		$user_info = $stmt->fetch(PDO::FETCH_ASSOC);
		//print_r($user_info);

		//查詢用戶的角色
		$stmt = $pdo->prepare("select * from user_role where user_id = :user_id");
		$stmt->execute(array(":user_id" => $_GET['user_id']));
		//這里只留下role_id
		$user_role_info = array_column( $stmt->fetchAll(PDO::FETCH_ASSOC),"role_id" );
		//print_r($user_role_info);

		//查詢所有角色
		$stmt = $pdo->prepare("select * from role");
		$stmt->execute();
		$role_arr = $stmt->fetchAll(PDO::FETCH_ASSOC);
		//print_r($role_arr);
	}

	//用來判斷復選框已選中
	function checked($i,$arr){
		if( in_array($i,$arr) ){
			echo "checked";
		}
	}

 ?>
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
</head>
<body>
	<div>
	<form action="" method='post'>
		<table>
			<caption>用戶信息</caption>
			<tr>
				<td>用戶名:</td>
				<td>
					<input type="hidden" name="user_id" value="<?php echo $user_info['user_id'] ?>">
					<?php echo $user_info['user_name']; ?>
				</td>
				
			</tr>
			<tr>
				<td>角色:</td>
				<?php if( count($role_arr) ):?>
					<?php foreach($role_arr as $role): ?>	
						<td>
							<div>
			<input type="checkbox" <?php checked($role['role_id'],$user_role_info);?> name="role[]" value="<?php echo $role['role_id'];?>"><?php echo $role['role_name'] ?>
							</div>
						</td>
					<?php endforeach; ?>
				<?php endif; ?>
			</tr>
			<tr>
				<td colspan="5">
					<input type="submit" name="editUser">
					<input type="button" value="返回主頁" onclick="location.href='./index.php'">
					<input type="button" value="返回用戶列表" onclick="location.href='./UserList.php'">
				</td>
			</tr>
		</table>
	</form>	
	</div>
</body>
</html>


<?php 
	if( isset($_POST['editUser'])){
		//獲取傳遞的role復選框數組,當將全部角色都撤銷時,傳遞的post數據中將不再有role,所以將其設為空數組。
		$edit_role = isset($_POST['role'])?$_POST['role']:array();
		$user_id = $_POST['user_id'];

		//增加的角色:
		$add_role = array_diff($edit_role,$user_role_info);

		//刪除的角色
		$sub_role = array_diff($user_role_info,$edit_role);

		//執行刪除角色
		$stmt = $pdo->prepare("delete from user_role where user_id = :user_id and role_id = :role_id");
		foreach($sub_role as $role_id){		
			$stmt->execute(array(":user_id"=>$user_id,":role_id"=>$role_id));	
		}

		//執行增加角色
		$stmt = $pdo->prepare("insert into user_role (user_id,role_id) values(:user_id,:role_id)");
		foreach($add_role as $role_id){
			$stmt->execute(array(":user_id"=>$user_id,":role_id"=>$role_id));
		}

		// echo "<script>location.href='editUser.php?user_id=$user_id</script>";
		echo "<script>location.replace(location.href);</script>";
	}
 ?>

  

其實看到這里,大概就知道RBAC是怎么簡單實現的了,下面幾個文件和上面的文件很相似;

 

 

/RBAC/backend/AddRole.php      添加一個角色

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>創建角色</title>
</head>
<body>
	<div id="container">
		<table>
			<form action="" method="post">
			<caption>角色信息</caption>
			<tr>
				<td>角色名:</td>
				<td><input type="text" name="role_name" style="width:300px"></td>
			</tr>
			<tr>
				<td colspan="2">
					<input type="submit" name="addrole" value="添加">
					<input type="button" value="返回首頁" onclick="location.href='./index.php'">
				</td>
			</tr>
			</form>
		</table>
	</div>
</body>
</html>


<?php 
	if( isset($_POST['addrole']) ){
		$role_name = $_POST['role_name'];
		$pdo = new PDO("mysql:host=127.0.0.1;dbname=rbac","root","123456");
		$stmt = $pdo->prepare("insert into role (role_name) values ( :role_name )");
		$stmt->execute(array(":role_name"=>$role_name));
	}
 ?>

  

 

/RBAC/backend/RoleList.php  顯示角色的列表

<?php 
	$pdo = new PDO("mysql:host=127.0.0.1;dbname=rbac","root","123456");
	$stmt = $pdo->prepare("select * from role");
	$stmt->execute();
	$role_arr = $stmt->fetchAll(PDO::FETCH_ASSOC);
 ?>

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>角色管理</title>
</head>
<body>
	<div>
		<table>
			<caption>角色列表</caption>
			<tr>
				<td>角色名</td>
				<td>操作</td>
			</tr>
			<?php if( count($role_arr) ): ?>
				<?php foreach($role_arr as $role): ?>
					 <tr>
					 	<td><?php echo $role['role_name']; ?></td>
					 	<td>
					 		<a href="EditRole.php?role_id=<?php echo $role['role_id']?>">權限設置</a>
					 	</td>
					 </tr>
				<?php endforeach; ?>
			<?php endif; ?>
		</table>
	</div>
</body>
</html>

  

/RBAC/backend/EditRole.php     編輯角色可以獲得哪些資源(權限access)

<?php 
	if( isset($_GET['role_id']) ){
		$pdo = new PDO("mysql:host=127.0.0.1;dbname=rbac","root","123456");

		//查詢角色信息
		$stmt = $pdo->prepare("select * from role where role_id = :role_id");
		$stmt->execute(array("role_id" => $_GET['role_id']));
		$role_info = $stmt->fetch(PDO::FETCH_ASSOC);
		//print_r($user_info);

		//查詢當前角色擁有的權限
		$stmt = $pdo->prepare("select * from role_access where role_id = :role_id");
		$stmt->execute(array(":role_id" => $_GET['role_id']));
		//這里只留下access_id
		$role_access_info = array_column( $stmt->fetchAll(PDO::FETCH_ASSOC),"access_id" );
		//print_r($user_role_info);

		//查詢所有的資源信息
		$stmt = $pdo->prepare("select * from access");
		$stmt->execute();
		$access_arr = $stmt->fetchAll(PDO::FETCH_ASSOC);
		//print_r($role_arr);
	}

	//用來判斷復選框已選中
	function checked($i,$arr){
		if( in_array($i,$arr) ){
			echo "checked";
		}
	}

 ?>
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>編輯角色</title>
</head>
<body>
	<div style="width:400px;margin:0 auto">
	<form action="" method='post'>
		<table>
			<caption>角色信息</caption>
			<tr>
				<td>角色名:</td>
				<td>
					<input type="hidden" name="role_id" value="<?php echo $role_info['role_id'] ?>">
					<?php echo $role_info['role_name']; ?>
				</td>
				
			</tr>
			<tr>
				<td>權限:</td>
				<?php if( count($access_arr) ):?>
						<td>
					<?php foreach($access_arr as $access): ?>	
							<div>
			<input type="checkbox" <?php checked($access['access_id'],$role_access_info);?> name="access[]" value="<?php echo $access['access_id']?>"><?php echo $access['title'] ?>
							</div>
					<?php endforeach; ?>
						</td>
				<?php endif; ?>
			</tr>
			<tr>
				<td colspan="5">
					<input type="submit" name="editRole">
					<input type="button" value="返回主頁" onclick="location.href='./index.php'">
					<input type="button" value="返回角色列表" onclick="location.href='./RoleList.php'">
				</td>
			</tr>
		</table>
	</form>	
	</div>
</body>
</html>


<?php 
	if( isset($_POST['editRole'])){
		//獲取傳遞的role復選框數組,當將全部角色都撤銷時,傳遞的post數據中將不再有role,所以將其設為空數組。
		$access = isset($_POST['access'])?$_POST['access']:array();
		$role_id = $_POST['role_id'];

		//增加的角色:
		$add_access = array_diff($access,$role_access_info);

		//刪除的角色
		$sub_access = array_diff($role_access_info,$access);

		//執行刪除角色
		$stmt = $pdo->prepare("delete from role_access where role_id = :role_id and access_id = :access_id");
		foreach($sub_access as $access_id){		
			$stmt->execute(array(":role_id"=>$role_id,":access_id"=>$access_id ));	
		}

		//執行增加角色
		$stmt = $pdo->prepare("insert into role_access (role_id,access_id) values(:role_id,:access_id)");
		foreach($add_access as $access_id){
			$stmt->execute(array(":role_id"=>$role_id,":access_id"=>$access_id ));	
		}

		echo "<script>location.replace(location.href);</script>";
	}
 ?>

  

 

/RBAC/backend/AddAccess.php  添加一個權限(資源)

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>創建訪問資源</title>
</head>
<body>
	<div>
		<table>
			<form action="" method="post">
			<caption>資源信息</caption>
			<tr>
				<td>資源名:</td>
				<td><input type="text" name="title" style="width:400px"></td>
			</tr>
			<tr>
				<td>URLs:</td>
				<td>
					<textarea style="margin-top:20px;width:400px;height:200px" name="urls"></textarea>
				</td>
			</tr>
			<tr>
				<td colspan="2">
					<input type="submit" name="addaccess" value="添加">
					<input type="button" value="返回首頁" onclick="location.href='./index.php'">
				</td>
			</tr>
			</form>
		</table>
	</div>
</body>
</html>


<?php 

	if( isset($_POST['addaccess']) ){
		$title = $_POST['title'];
		$urls = json_encode( explode(",",$_POST['urls']) );
		$pdo = new PDO("mysql:host=127.0.0.1;dbname=rbac","root","123456");
		$stmt = $pdo->prepare("insert into access (title,urls) values ( :title, :urls )");
		$stmt->execute(array(":title"=>$title,":urls"=>$urls));
	}
 ?>

  

 

/RBAC/backend/AccessList.php  資源(權限)列表

<?php 
	$pdo = new PDO("mysql:host=127.0.0.1;dbname=rbac","root","123456");
	$stmt = $pdo->prepare("select * from access");
	$stmt->execute();
	$access_arr = $stmt->fetchAll(PDO::FETCH_ASSOC);

	function print_json($data){
		$arr = json_decode($data,true);
		foreach($arr as $v ){
			echo $v."<br>";
		}
	}
 ?>

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>資源管理</title>
</head>
<body>
	<div id="container">
		<table>
			<caption>資源列表</caption>
			<tr>
				<td>資源ID</td>
				<td>名稱</td>
				<td>urls</td>
				<td>操作</td>
			</tr>
			<?php if( count($access_arr) ): ?>
				<?php foreach($access_arr as $access): ?>
					 <tr>
					 	<td><?php echo $access['access_id']; ?></td>
					 	<td><?php echo $access['title']; ?></td>
					 	<td><?php print_json($access['urls']); ?></td>
					 	<td>
					 		<a href="EditAccess.php?access_id=<?php echo $access['access_id']?>">資源設置</a>
					 	</td>
					 </tr>
				<?php endforeach; ?>
			<?php endif; ?>
		</table>
	</div>
</body>
</html>

  

到這里,基本的權限系統就已經建立好了,但是缺少實例,而且還有檢測權限的操作。

 

/RBAC/frontend/index.php  前台首頁

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>首頁</title>
</head>
<body>
	<h2><a href="./pageone.php">pageone.php</a></h2>
	<h2><a href="./pagetwo.php">pagetwo.php</a></h2>
	<h2><a href="./pagethree.php">pagethree.php</a></h2>
</body>
</html>

  

/RBAC/frontend/pageone.php  測試頁面1

<?php include "checkPermission.php" ?>
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>pageOne</title>
</head>
<body>
	<h1>pageOne</h1>
	<input type="button" onclick="history.back()" value="返回">
</body>
</html>

  

/RBAC/frontend/pagetwo.php   測試頁面2

<?php include "checkPermission.php" ?>
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>pageTwo</title>
</head>
<body>
	<h1>pageTwo</h1>
	<input type="button" onclick="history.back()" value="返回">
</body>
</html>

  

/RBAC/frontend/pagethree.php  測試頁面3

<?php include "checkPermission.php" ?>
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>pageThree</title>
</head>
<body>
	<h1>pageThree</h1>
	<input type="button" onclick="history.back()" value="返回">
</body>
</html>

  

/RBAC/frontend/checkPermission.php  檢測權限

<?php 
	//此處為了測試,不進行檢查
// 真正使用的時候,應該使用下面一個php代碼
	$user_id="666";
	$user_name="beyond";
	$pdo = new PDO("mysql:host=127.0.0.1;dbname=rbac","root","123456");
	$stmt = $pdo->prepare("select count(*) from user where user_id=:user_id and user_name=:user_name;");
	$stmt->execute(array(":user_id"=>$user_id,":user_name"=>$user_name));
	$flag = $stmt->fetch(PDO::FETCH_ASSOC);

	!$flag && die("無此用戶");

	$stmt = $pdo->prepare("select * from user
				left join user_role on user.user_id = user_role.user_id
				right join role_access on user_role.role_id = role_access.role_id
				left join access on access.access_id = role_access.access_id
				where user.user_id = :user_id
				");
	$stmt->execute(array(":user_id"=>$user_id));

	$data = $stmt->fetchAll(PDO::FETCH_ASSOC);

	//urls用來保存當前用戶所擁有的權限url
	//可以保存在緩存中,提高效率
	$urls = array();
	foreach( $data as $v ){
		$urls[] = json_decode($v['urls'],true)[0];
	}

	//判斷對當前url是否有權限
	if ( ! in_array( $_SERVER['SCRIPT_NAME'], $urls ) ){
		include "error.html";
		exit;
	} 
 ?>


 <?php 
 /*

 	真正使用的時候,應該使用這個

 	//因為重點不是驗證登錄,所以這里簡單的驗證登錄
	if( $_SESSION['user_id'] && $_SESSION['user_name'] ){
		$pdo = new PDO("mysql:host=127.0.0.1;dbname=rbac","root","123456");
		$stmt = $pdo->prepare("select count(*) from user where user_id=:user_id and user_name=:user_name;");
		$stmt->execute(array(":user_id"=>$_SESSION['user_id'],":user_name"=>$_SESSION['user_name']));
		$flag = $stmt->fetch(PDO::FETCH_ASSOC);
		!$flag && die("無此用戶");
		$stmt = $pdo->prepare("select * from user
					left join user_role on user.user_id = user_role.user_id
					right join role_access on user_role.role_id = role_access.role_id
					left join access on access.access_id = role_access.access_id
					where user.user_id = :user_id
					");
		$stmt->execute(array("user_id"=>$_SESSION['user_id']));
		$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
		$urls = array();
		foreach( $data as $v ){
			$urls[] = json_decode($v['urls'],true)[0];
		}
		if ( ! in_array( $_SERVER['SCRIPT_NAME'], $urls ) ){
			include "error.html";
			exit;
		} 

	}

*/
 ?>

  

/RBAC/frontend/error.html      沒有權限時顯示

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>無權限</title>
</head>
<body>
	<h2>你沒有權限查看該頁面,請聯系管理員開通</h2>
</body>
</html>

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM