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
<?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权限控制实现原理——权限表、用户表与关联表设计的详细内容。