您好,登錄后才能下訂單哦!
本篇內容介紹了“RBAC權限控制的實現原理”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
RBAC是英文Role-based Access Control的首字母縮寫,中文意思是基礎角色的權限控制,它是一種思想,根據 RBAC 思想進行數據表設計,更好的完成不同角色的對應的權限控制。
如何使用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
<?php namespace 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;/**權限認證類**/
<?php use 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', // 權限規(guī)則表 '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 需要驗證的規(guī)則列表,支持逗號分隔的權限規(guī)則或索引數組 * @param integer $uid 認證用戶ID * @param string $relation 如果為 'or' 表示滿足任一條規(guī)則即通過驗證;如果為 'and' 則表示需滿足所有規(guī)則才能通過驗證 * @param string $mode 執(zhí)行check的模式 * @param integer $type 規(guī)則類型 * @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); // 解析規(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; } /** * 根據用戶ID獲取用戶組,返回值為數組 * @param integer $uid 用戶ID * @return array 用戶所屬用戶組 ['uid'=>'用戶ID', 'group_id'=>'用戶組ID', 'title'=>'用戶組名', 'rules'=>'用戶組擁有的規(guī)則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 規(guī)則類型 * @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 = []; // 保存用戶所屬用戶組設置的所有權限規(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] ]; // 讀取用戶組所有權限規(guī)則 $rules = Db::name($this->_config['auth_rule'])->where($map)->field('condition,name')->select(); // 循環(huán)規(guī)則,判斷結果。 $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]; } }
這樣就能實現路由操作權限的實時檢測,比如我們讓首頁控制器繼承中間控制器:
<?php namespace 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權限控制的實現原理”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。