共計 6491 個字符,預(yù)計需要花費 17 分鐘才能閱讀完成。
本篇內(nèi)容介紹了“RBAC 權(quán)限控制的實現(xiàn)原理”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓丸趣 TV 小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
RBAC 是英文 Role-based Access Control 的首字母縮寫,中文意思是基礎(chǔ)角色的權(quán)限控制,它是一種思想,根據(jù) RBAC 思想進(jìn)行數(shù)據(jù)表設(shè)計, 更好的完成不同角色的對應(yīng)的權(quán)限控制。
如何使用 RBAC 思想進(jìn)行數(shù)據(jù)表的設(shè)計
如果我們的項目允許一個后臺管理用戶可能有 1 個或者 2 個及 2 個以上的多個角色, 按照下面進(jìn)行設(shè)計:
權(quán)限菜單表,角色表,用戶表是互相獨立的。設(shè)計表的順序是權(quán)限菜單表,角色表,用戶表,用戶 - 角色關(guān)聯(lián)表。
1. 首先是權(quán)限菜單表設(shè)計如下:
注意:權(quán)限菜單可以是多級菜單,添加 pid 字段,方便無限極遞歸分類。
2. 角色表設(shè)計如下:
3. 用戶表設(shè)計如下:
4. 最后是用戶 - 角色關(guān)聯(lián)表設(shè)計如下:
當(dāng)超級管理員在后臺需要添加新用戶時,不僅需要 insert 數(shù)據(jù)進(jìn)用戶表,也需要在用戶 - 角色表中添加用戶和角色的關(guān)系。與之對應(yīng),刪除用戶時,也需要將用戶 - 角色表中對應(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 中儲存的 uid 判斷當(dāng)前登錄用戶的身份信息,根據(jù)獲取到的 uid 查詢用戶 - 角色關(guān)聯(lián)表查詢到用戶的角色 id, 然后到角色表獲取到該用戶可操作的權(quán)限菜單。
封裝中間控制器 Common.php
?php
namespace app\admin\controller;
use app\BaseController;
use think\facade\Session;
use lib\Auth;/** 權(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;
// 檢查到用戶登錄后, 還要檢測該用戶是否具有操作某個頁面的權(quán)限, (是否具有操作某個方法的權(quán)限)
}else{ $auth = new Auth();
if(!$auth- check(request()- controller(). / .request()- action(),$sess_auth)){
historyTo( 抱歉~ 你沒有操作該欄目的權(quán)限,請聯(lián)系管理員!
exit;
}
}
}
}
lib\Auth;/** 權(quán)限認(rèn)證類 **/
?php
use think\facade\Db;
use think\facade\Config;
use think\facade\Session;
use think\facade\Request;
protected $_config = [
auth_on = true, // 認(rèn)證開關(guān)
auth_type = 1, // 認(rèn)證方式,1 為實時認(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 需要驗證的規(guī)則列表,支持逗號分隔的權(quán)限規(guī)則或索引數(shù)組
* @param integer $uid 認(rèn)證用戶 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;
}
/**
* 根據(jù)用戶 ID 獲取用戶組,返回值為數(shù)組
* @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];
}
/**
* 獲得權(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)行驗證
$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];
}
}
這樣就能實現(xiàn)路由操作權(quán)限的實時檢測,比如我們讓首頁控制器繼承中間控制器:
?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
// 根據(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);
}
}
最終實現(xiàn)的效果如圖:
“RBAC 權(quán)限控制的實現(xiàn)原理”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注丸趣 TV 網(wǎng)站,丸趣 TV 小編將為大家輸出更多高質(zhì)量的實用文章!