RBAC權(quán)限控制實(shí)現(xiàn)原理——權(quán)限表、用戶表與關(guān)聯(lián)表設(shè)計(jì)

????????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ì):


RBAC權(quán)限控制實(shí)現(xiàn)原理——權(quán)限表、用戶表與關(guān)聯(lián)表設(shè)計(jì)

? ? 權(quán)限菜單表,角色表,用戶表是互相獨(dú)立的。設(shè)計(jì)表的順序是權(quán)限菜單表,角色表,用戶表,用戶-角色關(guān)聯(lián)表。

? ? 1. 首先是權(quán)限菜單表設(shè)計(jì)如下:

????注意:權(quán)限菜單可以是多級(jí)菜單,添加pid字段,方便無限極遞歸分類。?

RBAC權(quán)限控制實(shí)現(xiàn)原理——權(quán)限表、用戶表與關(guān)聯(lián)表設(shè)計(jì)

? ? 2. 角色表設(shè)計(jì)如下:

RBAC權(quán)限控制實(shí)現(xiàn)原理——權(quán)限表、用戶表與關(guān)聯(lián)表設(shè)計(jì)

? ? 3. 用戶表設(shè)計(jì)如下:

RBAC權(quán)限控制實(shí)現(xiàn)原理——權(quán)限表、用戶表與關(guān)聯(lián)表設(shè)計(jì)

? ? 4. 最后是用戶-角色關(guān)聯(lián)表設(shè)計(jì)如下:

RBAC權(quán)限控制實(shí)現(xiàn)原理——權(quán)限表、用戶表與關(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 thinkacadeSession; use libAuth;/**權(quán)限認(rèn)證類**/ class Common extends BaseController {     public function initialize(){         $sess_auth  = session(&#39;uid&#39;);         $uname = session(&#39;uname&#39;);                //判斷用戶是否登錄         if(!$sess_auth){             jumpTo(&#39;/login/index&#39;);             exit;             //檢查到用戶登錄后, 還要檢測(cè)該用戶是否具有操作某個(gè)頁面的權(quán)限, (是否具有操作某個(gè)方法的權(quán)限)         }else{             $auth = new Auth();             if(!$auth->check(request()-&gt;controller().'/'.request()-&gt;action(),$sess_auth)){ ????????????????historyTo('抱歉~你沒有操作該欄目的權(quán)限,請(qǐng)聯(lián)系管理員!'); ????????????????exit; ????????????} ????????} ????} }

????libAuth;/**權(quán)限認(rèn)證類**/
????

<?php use thinkacadeDb; use thinkacadeConfig; use thinkacadeSession; use thinkacadeRequest;    class Auth {     protected $_config = [         &#39;auth_on&#39;           =>??true,????????????????//?認(rèn)證開關(guān) ????????'auth_type'?????????=&gt;??1,???????????????????//?認(rèn)證方式,1為實(shí)時(shí)認(rèn)證;2為登錄認(rèn)證。 ????????'auth_role'????????=&gt;??'auth_role',????????//?用戶組數(shù)據(jù)表名 ????????'users_role'?=&gt;???'users_role',?//?用戶-用戶組關(guān)系表 ????????'auth_rule'?????????=&gt;??'auth_rule',?????????//?權(quán)限規(guī)則表 ????????'auth_user'?????????=&gt;??'users',?????????????//?用戶信息表 ???????? ????]; ???? ????public?function?__construct() ????{ ????????if?(Config::get('app.auth'))?{ ????????????$this-&gt;_config?=?array_merge($this-&gt;_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-&gt;_config['auth_on'])?{ ????????????return?true; ????????} ????????$authList?=?$this-&gt;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'?&amp;&amp;?$query?!=?$auth)?{ ????????????????parse_str($query,?$param);?//?解析規(guī)則中的param ????????????????$intersect?=?array_intersect_assoc($REQUEST,?$param); ????????????????$auth?=?preg_replace('/?.*$/U',?'',?$auth); ????????????????if?(in_array($auth,?$name)?&amp;&amp;?$intersect?==?$param)?{ ????????????????????$list[]?=?$auth; ????????????????} ????????????}?elseif?(in_array($auth,?$name))?{ ????????????????$list[]?=?$auth; ????????????} ????????} ????????if?($relation?===?'or'?&amp;&amp;?!empty($list))?{ ????????????return?true; ????????} ????????$diff?=?array_diff($name,?$list); ????????if?($relation?===?'and'?&amp;&amp;?empty($diff))?{ ????????????return?true; ????????} ????????return?false; ????} ????/** ?????*?根據(jù)用戶ID獲取用戶組,返回值為數(shù)組 ?????*?@param??integer?$uid?用戶ID ?????*?@return?array??????用戶所屬用戶組?['uid'=&gt;'用戶ID',?'group_id'=&gt;'用戶組ID',?'title'=&gt;'用戶組名',?'rules'=&gt;'用戶組擁有的規(guī)則ID,多個(gè)用英文,隔開'] ?????*/ ????public?function?getGroups($uid) ????{ ????????static?$groups?=?[]; ????????if?(isset($groups[$uid]))?{ ????????????return?$groups[$uid]; ????????} ????????$user_groups?=?Db::name($this-&gt;_config['users_role']) ????????????-&gt;alias('ur') ????????????-&gt;where('ur.uid',?$uid) ????????????-&gt;where('ar.status',?1) ????????????-&gt;join($this-&gt;_config['auth_role'].'?ar',?"ur.role_id?=?ar.id") ????????????-&gt;field('uid,role_id,title,rules') ????????????-&gt;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-&gt;_config['auth_type']?==?2?&amp;&amp;?Session::has('_AUTH_LIST_'.$uid.$t))?{ ????????????return?Session::get('_AUTH_LIST_'.$uid.$t); ????????} ????????//?讀取用戶所屬用戶組 ????????$groups?=?$this-&gt;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-&gt;_config['auth_rule'])-&gt;where($map)-&gt;field('condition,name')-&gt;select(); ????????//?循環(huán)規(guī)則,判斷結(jié)果。 ????????$authList?=?[]; ????????foreach?($rules?as?$rule)?{ ????????????if?(!empty($rule['condition']))?{?//?根據(jù)condition進(jìn)行驗(yàn)證 ????????????????$user?=?$this-&gt;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-&gt;_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-&gt;config['auth_user']); ????????//?獲取用戶表主鍵 ????????$_pk?=?is_string($user-&gt;getPk())???$user-&gt;getPk()?:?'uid'; ????????if?(!isset($user_info[$uid]))?{ ????????????$user_info[$uid]?=?$user-&gt;where($_pk,?$uid)-&gt;find(); ????????}  ????????return?$user_info[$uid]; ????} }

? ? 這樣就能實(shí)現(xiàn)路由操作權(quán)限的實(shí)時(shí)檢測(cè),比如我們讓首頁控制器繼承中間控制器:

<?php namespace appdmincontroller; use thinkRequest; use thinkacadeDb;//db類 use thinkacadeSession; use libRule; class Index extends Common {     public function index()     {          $uname = session(&#39;uname&#39;);         $uid = session(&#39;uid&#39;);         // 根據(jù)uid,獲取該用戶相應(yīng)的權(quán)限,連表查詢         $res = Db::name(&#39;users&#39;)->alias('u')-&gt;where('u.uid',$uid) ????????-&gt;leftJoin('users_role?ur','ur.uid?=?u.uid') ????????-&gt;leftJoin('auth_role?ar','ar.id?=?ur.role_id') ????????-&gt;field('u.uid,u.uname,ar.rules') ????????-&gt;select()-&gt;toArray(); ????????//?dd($res); ????????$rules?=?implode(",",array_column($res,'rules')); ????????//?dd(?$rules); ????????//in查詢?根據(jù)獲取到的rules?id?選權(quán)限列表 ????????$res?=?Db::name('auth_rule')-&gt;field('id,name,title,pid')-&gt;order('id','asc')-&gt;where('is_menu',1) ????????-&gt;where('id','in',$rules)-&gt;select(); ????????//?dump($res); ????????//這里使用擴(kuò)展類Rule中封裝的無限極分類方法 ????????$rlist?=?Rule::Rulelayer($res); ????????//?dd($rlist); ????????$data?=?[ ????????????'uid'=&gt;$uid, ????????????'uname'=&gt;$uname, ????????????'rlist'=&gt;$rlist, ????????????'create_time'=&gt;1617252175 ????????]; ????????????return?view('index',?$data); ???????????? ????} ?}

最終實(shí)現(xiàn)的效果如圖:

RBAC權(quán)限控制實(shí)現(xiàn)原理——權(quán)限表、用戶表與關(guān)聯(lián)表設(shè)計(jì)


? 版權(quán)聲明
THE END
喜歡就支持一下吧
點(diǎn)贊5 分享