• 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="400px"></td>
    			</tr>
    			<tr>
    				<td>用户名:</td>
    				<td><input type="text" name="user_name" style="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="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="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="400px"></td>
    			</tr>
    			<tr>
    				<td>URLs:</td>
    				<td>
    					<textarea style="margin-top:20px;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>
    

      

  • 相关阅读:
    ADO连接access和oracle的一个区别
    我的大救星——Oracle APEX 快速Web开发(鼠标点点即可开发出专业级Web应用)
    Sql Server 2008 R2 error:40 错误处理
    我的第一个WPF程序
    开源社会网络分析工具NodeXL介绍
    JavaScript学习笔记1之基础知识点
    JavaScript学习笔记6 之经典神坑题整理
    JavaScript学习笔记2之Tab切换
    变量声明和函数声明会提升到当前作用域顶部
    JavaScript学习笔记5 之 计时器 & scroll、offset、client系列属性 & 图片无缝滚动
  • 原文地址:https://www.cnblogs.com/-beyond/p/8422108.html
Copyright © 2020-2023  润新知