如何使用RBAC思想进行数据表的设计
如果我们的项目允许一个后台管理用户可能有1个或者2个及2个以上的多个角色,按照下面进行设计:
权限菜单表,角色表,用户表是互相独立的。设计表的顺序是权限菜单表,角色表,用户表,用户-角色关联表。
1. 首先是权限菜单表设计如下:
注意:权限菜单可以是多级菜单,添加pID字段,方便无限极递归分类。
2. 角色表设计如下:
3. 用户表设计如下:
4. 最后是用户-角色关联表设计如下:
当超级管理员在后台需要添加新用户时,不仅需要insert数据进用户表,也需要在用户-角色表中添加用户和角色的关系。与之对应,删除用户时,也需要将用户-角色表中对应的用户-角色关系删除。
public function add() { if(request()->isPost()){ $role_ID = input('post.role_ID'); $data = [ 'uname'=>input('post.uname'), 'pwd'=>password_hash(input('post.pwd'),PASSWORD_BCRYPT), 'login_ip'=>request()->ip(), 'status'=>input('post.status'), 'create_time'=>time(), ]; $uID = Db::name('users')->insertGetID($data); if($uID){ $data = [ 'uID'=>$uID, 'role_ID'=>$role_ID ]; $ID = Db::name('users_role')->insertGetID($data); if($ID) { echo 'true'; exit; }else{ echo 'false'; exit; } }else{ echo 'false'; exit; } }else{ //获取所有角色 $role = Db::name('auth_role')->fIEld('ID,Title')->order('ID','asc')->where('status',1)->select(); return vIEw('add',['role'=>$role]); } }
这样以来我们根据用户登录以后session中储存的uID判断当前登录用户的身份信息,根据获取到的uID查询用户-角色关联表查询到用户的角色ID, 然后到角色表获取到该用户可 *** 作的权限菜单。
封装中间控制器Common.PHP
<?PHPnamespace app\admin\controller;use app\BaseController;use think\facade\Session;use lib\Auth;/**权限认证类**/class Common extends BaseController{ public function initialize(){ $sess_auth = session('uID'); $uname = session('uname'); //判断用户是否登录 if(!$sess_auth){ jumpTo('/login/index'); exit; //检查到用户登录后, 还要检测该用户是否具有 *** 作某个页面的权限, (是否具有 *** 作某个方法的权限) }else{ $auth = new Auth(); if(!$auth->check(request()->controller().'/'.request()->action(),$sess_auth)){ historyTo('抱歉~你没有 *** 作该栏目的权限,请联系管理员!'); exit; } } }}
lib\Auth;/**权限认证类**/
<?PHPuse think\facade\Db;use think\facade\Config;use think\facade\Session;use think\facade\Request;class Auth{ protected $_config = [ 'auth_on' => true, // 认证开关 'auth_type' => 1, // 认证方式,1为实时认证;2为登录认证。 'auth_role' => 'auth_role', // 用户组数据表名 'users_role' => 'users_role', // 用户-用户组关系表 'auth_rule' => 'auth_rule', // 权限规则表 'auth_user' => 'users', // 用户信息表 ]; public function __construct() { if (Config::get('app.auth')) { $this->_config = array_merge($this->_config, Config::get('app.auth')); } } /** * 检查权限 * @param string|array $name 需要验证的规则列表,支持逗号分隔的权限规则或索引数组 * @param integer $uID 认证用户ID * @param string $relation 如果为 'or' 表示满足任一条规则即通过验证;如果为 'and' 则表示需满足所有规则才能通过验证 * @param string $mode 执行check的模式 * @param integer $type 规则类型 * @return boolean 通过验证返回true;失败返回false */ public function check($name, $uID, $relation = 'or', $mode = 'url', $type = 1) { if (!$this->_config['auth_on']) { return true; } $authList = $this->getAuthList($uID, $type); if (is_string($name)) { $name = strtolower($name); if (strpos($name, ',') !== false) { $name = explode(',', $name); } else { $name = [$name]; } } $List = []; if ($mode === 'url') { $REQUEST = unserialize(strtolower(serialize($_REQUEST))); } foreach ($authList as $auth) { $query = preg_replace('/^.+\?/U', '', $auth); if ($mode === 'url' && $query != $auth) { parse_str($query, $param); // 解析规则中的param $intersect = array_intersect_assoc($REQUEST, $param); $auth = preg_replace('/\?.*$/U', '', $auth); if (in_array($auth, $name) && $intersect == $param) { $List[] = $auth; } } elseif (in_array($auth, $name)) { $List[] = $auth; } } if ($relation === 'or' && !empty($List)) { return true; } $diff = array_diff($name, $List); if ($relation === 'and' && empty($diff)) { return true; } return false; } /** * 根据用户ID获取用户组,返回值为数组 * @param integer $uID 用户ID * @return array 用户所属用户组 ['uID'=>'用户ID', 'group_ID'=>'用户组ID', 'Title'=>'用户组名', 'rules'=>'用户组拥有的规则ID,多个用英文,隔开'] */ public function getGroups($uID) { static $groups = []; if (isset($groups[$uID])) { return $groups[$uID]; } $user_groups = Db::name($this->_config['users_role']) ->alias('ur') ->where('ur.uID', $uID) ->where('ar.status', 1) ->join($this->_config['auth_role'].' ar', "ur.role_ID = ar.ID") ->fIEld('uID,role_ID,Title,rules') ->select(); $groups[$uID] = $user_groups ?: []; return $groups[$uID]; } /** * 获得权限列表 * @param integer $uID 用户ID * @param integer $type 规则类型 * @return array 权限列表 */ protected function getAuthList($uID, $type) { static $_authList = []; $t = implode(',', (array)$type); if (isset($_authList[$uID.$t])) { return $_authList[$uID.$t]; } if ($this->_config['auth_type'] == 2 && Session::has('_AUTH_List_'.$uID.$t)) { return Session::get('_AUTH_List_'.$uID.$t); } // 读取用户所属用户组 $groups = $this->getGroups($uID); $IDs = []; // 保存用户所属用户组设置的所有权限规则ID foreach ($groups as $g) { $IDs = array_merge($IDs, explode(',', trim($g['rules'], ','))); } $IDs = array_unique($IDs); if (empty($IDs)) { $_authList[$uID.$t] = []; return []; } $map = [ ['ID', 'in', $IDs], ['type', '=', $type], ['status', '=', 1] ]; // 读取用户组所有权限规则 $rules = Db::name($this->_config['auth_rule'])->where($map)->fIEld('condition,name')->select(); // 循环规则,判断结果。 $authList = []; foreach ($rules as $rule) { if (!empty($rule['condition'])) { // 根据condition进行验证 $user = $this->getUserInfo($uID); // 获取用户信息,一维数组 $command = preg_replace('/\{(\w*?)\}/', '$user[\'\1\']', $rule['condition']); // dump($command); // deBUG @(eval('$condition=('.$command.');')); if ($condition) { $authList[] = strtolower($rule['name']); } } else { // 只要存在就记录 $authList[] = strtolower($rule['name']); } } $_authList[$uID.$t] = $authList; if ($this->_config['auth_type'] == 2) { Session::set('_AUTH_List_'.$uID.$t, $authList); } return array_unique($authList); } /** * 获得用户资料,根据自己的情况读取数据库 */ protected function getUserInfo($uID) { static $user_info = []; $user = Db::name($this->config['auth_user']); // 获取用户表主键 $_pk = is_string($user->getPk()) ? $user->getPk() : 'uID'; if (!isset($user_info[$uID])) { $user_info[$uID] = $user->where($_pk, $uID)->find(); } return $user_info[$uID]; }}
这样就能实现路由 *** 作权限的实时检测,比如我们让首页控制器继承中间控制器:
<?PHPnamespace app\admin\controller;use think\Request;use think\facade\Db;//db类use think\facade\Session;use lib\Rule;class Index extends Common{ public function index() { $uname = session('uname'); $uID = session('uID'); // 根据uID,获取该用户相应的权限,连表查询 $res = Db::name('users')->alias('u')->where('u.uID',$uID) ->leftJoin('users_role ur','ur.uID = u.uID') ->leftJoin('auth_role ar','ar.ID = ur.role_ID') ->fIEld('u.uID,u.uname,ar.rules') ->select()->toArray(); // dd($res); $rules = implode(",",array_column($res,'rules')); // dd( $rules); //in查询 根据获取到的rules ID 选权限列表 $res = Db::name('auth_rule')->fIEld('ID,name,Title,pID')->order('ID','asc')->where('is_menu',1) ->where('ID','in',$rules)->select(); // dump($res); //这里使用扩展类Rule中封装的无限极分类方法 $rList = Rule::Rulelayer($res); // dd($rList); $data = [ 'uID'=>$uID, 'uname'=>$uname, 'rList'=>$rList, 'create_time'=>1617252175 ]; return vIEw('index', $data); } }
最终实现的效果如图:
总结
以上是内存溢出为你收集整理的RBAC权限控制实现原理——权限表、用户表与关联表设计全部内容,希望文章能够帮你解决RBAC权限控制实现原理——权限表、用户表与关联表设计所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)