????????rbac是英文role-based access control的首字母縮寫,中文意思是基礎(chǔ)角色的權(quán)限控制,它是一種思想,根據(jù) rbac 思想進(jìn)行數(shù)據(jù)表設(shè)計(jì),更好的完成不同角色的對(duì)應(yīng)的權(quán)限控制。
????????
????????
????????如何使用RBAC思想進(jìn)行數(shù)據(jù)表的設(shè)計(jì)?
????????如果我們的項(xiàng)目允許一個(gè)后臺(tái)管理用戶可能有1個(gè)或者2個(gè)及2個(gè)以上的多個(gè)角色,按照下面進(jìn)行設(shè)計(jì):
? ? 權(quán)限菜單表,角色表,用戶表是互相獨(dú)立的。設(shè)計(jì)表的順序是權(quán)限菜單表,角色表,用戶表,用戶-角色關(guān)聯(lián)表。
? ? 1. 首先是權(quán)限菜單表設(shè)計(jì)如下:
????注意:權(quán)限菜單可以是多級(jí)菜單,添加pid字段,方便無限極遞歸分類。?
? ? 2. 角色表設(shè)計(jì)如下:
? ? 3. 用戶表設(shè)計(jì)如下:
? ? 4. 最后是用戶-角色關(guān)聯(lián)表設(shè)計(jì)如下:
????
????
????當(dāng)超級(jí)管理員在后臺(tái)需要添加新用戶時(shí),不僅需要insert數(shù)據(jù)進(jìn)用戶表,也需要在用戶-角色表中添加用戶和角色的關(guān)系。與之對(duì)應(yīng),刪除用戶時(shí),也需要將用戶-角色表中對(duì)應(yīng)的用戶-角色關(guān)系刪除。
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]); ????????} ??????? ????}
? ? ? ? ? ?這樣以來我們根據(jù)用戶登錄以后session中儲(chǔ)存的uid判斷當(dāng)前登錄用戶的身份信息,根據(jù)獲取到的uid查詢用戶-角色關(guān)聯(lián)表查詢到用戶的角色id, 然后到角色表獲取到該用戶可操作的權(quán)限菜單。
????封裝中間控制器Common.php
<?php namespace appdmincontroller; use appBaseController; use thinkacadeSession; use libAuth;/**權(quán)限認(rèn)證類**/ class Common extends BaseController { public function initialize(){ $sess_auth = session('uid'); $uname = session('uname'); //判斷用戶是否登錄 if(!$sess_auth){ jumpTo('/login/index'); exit; //檢查到用戶登錄后, 還要檢測(cè)該用戶是否具有操作某個(gè)頁面的權(quán)限, (是否具有操作某個(gè)方法的權(quán)限) }else{ $auth = new Auth(); if(!$auth->check(request()->controller().'/'.request()->action(),$sess_auth)){ ????????????????historyTo('抱歉~你沒有操作該欄目的權(quán)限,請(qǐng)聯(lián)系管理員!'); ????????????????exit; ????????????} ????????} ????} }
????libAuth;/**權(quán)限認(rèn)證類**/
????
<?php use thinkacadeDb; use thinkacadeConfig; use thinkacadeSession; use thinkacadeRequest; class Auth { protected $_config = [ 'auth_on' =>??true,????????????????//?認(rèn)證開關(guān) ????????'auth_type'?????????=>??1,???????????????????//?認(rèn)證方式,1為實(shí)時(shí)認(rèn)證;2為登錄認(rèn)證。 ????????'auth_role'????????=>??'auth_role',????????//?用戶組數(shù)據(jù)表名 ????????'users_role'?=>???'users_role',?//?用戶-用戶組關(guān)系表 ????????'auth_rule'?????????=>??'auth_rule',?????????//?權(quán)限規(guī)則表 ????????'auth_user'?????????=>??'users',?????????????//?用戶信息表 ???????? ????]; ???? ????public?function?__construct() ????{ ????????if?(Config::get('app.auth'))?{ ????????????$this->_config?=?array_merge($this->_config,?Config::get('app.auth')); ????????} ????} ???? ????/** ?????*?檢查權(quán)限 ?????*?@param??string|array??$name?????需要驗(yàn)證的規(guī)則列表,支持逗號(hào)分隔的權(quán)限規(guī)則或索引數(shù)組 ?????*?@param??integer??$uid??????認(rèn)證用戶ID ?????*?@param??string???$relation?如果為?'or'?表示滿足任一條規(guī)則即通過驗(yàn)證;如果為?'and'?則表示需滿足所有規(guī)則才能通過驗(yàn)證 ?????*?@param??string???$mode?????執(zhí)行check的模式 ?????*?@param??integer??$type?????規(guī)則類型 ?????*?@return?boolean???????????通過驗(yàn)證返回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);?//?解析規(guī)則中的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; ????} ????/** ?????*?根據(jù)用戶ID獲取用戶組,返回值為數(shù)組 ?????*?@param??integer?$uid?用戶ID ?????*?@return?array??????用戶所屬用戶組?['uid'=>'用戶ID',?'group_id'=>'用戶組ID',?'title'=>'用戶組名',?'rules'=>'用戶組擁有的規(guī)則ID,多個(gè)用英文,隔開'] ?????*/ ????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]; ????} ????/** ?????*?獲得權(quán)限列表 ?????*?@param??integer?$uid??用戶ID ?????*?@param??integer?$type?規(guī)則類型 ?????*?@return?array???????權(quán)限列表 ?????*/ ????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?=?[];?//?保存用戶所屬用戶組設(shè)置的所有權(quán)限規(guī)則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] ????????]; ????????//?讀取用戶組所有權(quán)限規(guī)則 ????????$rules?=?Db::name($this->_config['auth_rule'])->where($map)->field('condition,name')->select(); ????????//?循環(huán)規(guī)則,判斷結(jié)果。 ????????$authList?=?[]; ????????foreach?($rules?as?$rule)?{ ????????????if?(!empty($rule['condition']))?{?//?根據(jù)condition進(jìn)行驗(yàn)證 ????????????????$user?=?$this->getUserInfo($uid);?//?獲取用戶信息,一維數(shù)組 ????????????????$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); ????} ????/** ?????*?獲得用戶資料,根據(jù)自己的情況讀取數(shù)據(jù)庫 ?????*/ ????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]; ????} }
? ? 這樣就能實(shí)現(xiàn)路由操作權(quán)限的實(shí)時(shí)檢測(cè),比如我們讓首頁控制器繼承中間控制器:
<?php namespace appdmincontroller; use thinkRequest; use thinkacadeDb;//db類 use thinkacadeSession; use libRule; class Index extends Common { public function index() { $uname = session('uname'); $uid = session('uid'); // 根據(jù)uid,獲取該用戶相應(yīng)的權(quán)限,連表查詢 $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查詢?根據(jù)獲取到的rules?id?選權(quán)限列表 ????????$res?=?Db::name('auth_rule')->field('id,name,title,pid')->order('id','asc')->where('is_menu',1) ????????->where('id','in',$rules)->select(); ????????//?dump($res); ????????//這里使用擴(kuò)展類Rule中封裝的無限極分類方法 ????????$rlist?=?Rule::Rulelayer($res); ????????//?dd($rlist); ????????$data?=?[ ????????????'uid'=>$uid, ????????????'uname'=>$uname, ????????????'rlist'=>$rlist, ????????????'create_time'=>1617252175 ????????]; ????????????return?view('index',?$data); ???????????? ????} ?}
最終實(shí)現(xiàn)的效果如圖: