一、RBAC的前期准备
RBAC的权限管理需要知道怎么给用户分配角色,给角色分配权限,以权限来精细化需要的操作,判断是否有权限来操作这一步,达到管理权限的目的。
先展示下要达到的效果 :
左边为三剑客:用户、角色、权限;
下面为测试页面
二、RBAC的数据表
用户表用的是yii2-admin中migration里面的用户表
create table `user`
(
`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`username` varchar(32) NOT NULL,
`auth_key` varchar(32) NOT NULL,
`password_hash` varchar(256) NOT NULL,
`password_reset_token` varchar(256),
`email` varchar(256) NOT NULL,
`status` integer not null default 1,
`created_at` integer not null,
`updated_at` integer not null,
KEY `email` (`email`)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `role` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL DEFAULT '' COMMENT '角色名称',
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态 1:有效 0:无效',
`updated_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '最后一次更新时间',
`created_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '插入时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='角色表';
CREATE TABLE `permission` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(50) NOT NULL DEFAULT '' COMMENT '权限名称',
`urls` varchar(1000) NOT NULL DEFAULT '' COMMENT 'json 数组',
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态 1:有效 0:无效',
`updated_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '最后一次更新时间',
`created_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '插入时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='权限详情表';
CREATE TABLE `user_role` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`uid` int(11) NOT NULL DEFAULT '0' COMMENT '用户id',
`role_id` int(11) NOT NULL DEFAULT '0' COMMENT '角色ID',
`created_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '插入时间',
PRIMARY KEY (`id`),
KEY `uid` (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户角色表';
CREATE TABLE `role_permisson` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`role_id` int(11) NOT NULL DEFAULT '0' COMMENT '角色id',
`permission_id` int(11) NOT NULL DEFAULT '0' COMMENT '权限id',
`created_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '插入时间',
PRIMARY KEY (`id`),
KEY `role_id` (`role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='角色权限表';
三、视图渲染部分。
将准备好的页面放到views层,在models层建立和表相对应的文件
文件结构:
models层:
view层:
四、逻辑部分:
以角色的增加和修改、给角色增加权限为例:
1、RoleController代码:
<?php
namespace app\controllers;
use Yii;
use app\models\Permission;
use app\models\Role;
use app\models\RolePermission;
class RoleController extends BaseController
{
public $layout = 'rbac';
/*
*方法讲解:
*获取有效的角色在视图角色列表渲染
*/
public function actionIndex(){
$roles = Role::find()->where([ 'status' => 1 ])->orderBy(['id'=>SORT_DESC])->all();
return $this->render('index',['roles'=>$roles]);
}
/*
*方法讲解:
*编辑+新增
*1、如果能获取到角色的id。则从数据库取出数据来渲染。若没有则渲染不显示数据
*2、提交更新相应的修改或者增加。同样以id来区分
*3、新增方法简化还可用load()方法
*/
public function actionEdit(){
if (Yii::$app->request->isPost){
//这个request方法是封装的获取参数的方法在后面的BaseController里面可以找到
$id = $this->request("id",0);
$name = $this->request("name","");
$date_now = date("Y-m-d H:i:s");
//验证参数TODO:
$role = Role::find()->where([ 'id' => $id ])->one();
if( $role ){//编辑动作
$role = $role;
}else{//添加动作
$role = new Role();
$role->status = 1;
$role->created_time = $date_now;
}
$role->name = $name;
$role->updated_time = $date_now;
$res = $role->save(0);
if ($res){
return $this->redirect('/role/index');
}
}
$id = $this->request("id",0);
$role = [];
if( $id ){
$role = Role::find()->where([ 'id' => $id ])->one();
}
return $this->render("edit",[
"role" => $role
]);
}
/*
*最重要的一步:展示权限和该角色已经拥有的权限
*/
public function actionSet(){
if (Yii::$app->request->isPost){
//实现保存选中权限的逻辑
$id = $this->request("id",0);
$permission_ids = $this->request("permission_ids",[]);
//验证参数TODO:
//取出所有已分配给指定角色的权限
$role_permission_list = RolePermission::find()->where([ 'role_id' => $id ])->asArray()->all();
$assign_permission_ids = array_column( $role_permission_list,'permission_id' );
/**
* 找出删除的权限
* 假如已有的权限集合是A,界面传递过得权限集合是B
* 权限集合A当中的某个权限不在权限集合B当中,就应该删除
* 使用 array_diff() 计算补集
*/
$delete_permission_ids = array_diff( $assign_permission_ids,$permission_ids );
if( $delete_permission_ids ){
RolePermission::deleteAll([ 'role_id' => $id,'permission_id' => $delete_permission_ids ]);
}
/**
* 找出添加的权限
* 假如已有的权限集合是A,界面传递过得权限集合是B
* 权限集合B当中的某个权限不在权限集合A当中,就应该添加
* 使用 array_diff() 计算补集
*/
$new_permission_ids = array_diff( $permission_ids,$assign_permission_ids );
if( $new_permission_ids ){
foreach( $new_permission_ids as $permission_id ){
$role_permission = new RolePermission();
$role_permission->role_id = $id;
$role_permission->permission_id = $permission_id;
$role_permission->created_time = date("Y-m-d H:i:s");
$role_permission->save( 0 );
}
}
return $this->redirect('/role/index');
}
$id = $this->request("id",0);
//验证数据TODO:
$role = Role::find()->where([ 'id' => $id ])->one();
//取出所有的权限
$permissions = Permission::find()->where([ 'status' => 1 ])->orderBy( [ 'id' => SORT_DESC ])->all();
//取出所有已分配的权限
$role_permission= RolePermission::find()->where([ 'role_id' => $id ])->asArray()->all();
$role_permission_ids = array_column( $role_permission,"permission_id" );
return $this->render("set",[
"role" => $role,
'permissions' => $permissions,
"role_permission_ids" => $role_permission_ids
]);
}
}
2、由于基础的增加改查都会编写。在这里写set模板渲染
set.php在view中的role目录,代码如下:
<div class="row">
<form class="form-horizontal tasi-form" method="POST" action="<?= Url::to('/role/set') ?>">
<div class="form-group">
<label class="col-sm-2 control-label col-lg-2" for="inputSuccess">角色</label>
<div class="col-lg-10">
<?php if( $permissions ):?>
<?php foreach( $permissions as $permission ):?>
<div class="checkbox">
<label>
<input type="checkbox" name="permission_ids[]" value="<?=$permission['id'];?>"
<?php if( in_array( $permission['id'] ,$role_permission_ids ) ):?> checked <?php endif;?>
/>
<?=$permission['title'];?>
</label>
</div>
<?php endforeach;?>
<?php endif;?>
<input type="hidden" name="id" value="<?=$role?$role['id']:0;?>">
<button type="submit" class="btn btn-default">Submit</button>
</div>
</div>
</form>
</div>
给三剑客增加、修改、删除的核心代码已经结束
五、基础控制器的编写:
实现辨别是否有权限访问要访问的页面
BaseController
<?php
namespace app\controllers;
use Yii;
use yii\filters\AccessControl;
use yii\web\Controller;
use yii\filters\VerbFilter;
use app\models\Permission;
use app\models\RolePermission;
use app\models\User;
use app\models\UserRole;
use yii\helpers\Url;
class BaseController extends Controller
{
protected $allowAllAction = [
'site/login',
];
public $ignore_url = [
'test/forbidden' ,
'user/login'
];
public $privilege_urls = [];//保存有效的权限链接
// 本系统所有页面都是需要登录之后才能访问的, 在框架中加入统一验证方法
public function beforeAction($action) {
$login_status = $this->checkLoginStatus();
if ( !$login_status && !in_array( $action->uniqueId,$this->allowAllAction ) ) {
$this->redirect( "/site/login" );//返回到登录页面
return false;
}
// *
// * 判断权限的逻辑是
// * 取出当前登录用户的所属角色,
// * 在通过角色 取出 所属 权限关系
// * 在权限表中取出所有的权限链接
// * 判断当前访问的链接 是否在 所拥有的权限列表中
//判断当前访问的链接 是否在 所拥有的权限列表中
if( !$this->checkPrivilege( $action->getUniqueId() ) ){
$this->redirect( "/test/forbidden");
return false;
}
return true;
}
//检查是否有访问指定链接的权限
public function checkPrivilege( $url ){
//如果是超级管理员 也不需要权限判断,这里可以根据自己需要更改
if( Yii::$app->user->identity->id == 1 ){
return true;
}
//有一些页面是不需要进行权限判断的
if( in_array( $url,$this->ignore_url ) ){
return true;
}
return in_array( $url, $this->getRolePrivilege( ) );
}
/*
* 获取某用户的所有权限
* 取出指定用户的所属角色,
* 在通过角色 取出 所属 权限关系
* 在权限表中取出所有的权限链接
*/
public function getRolePrivilege($uid = 0){
if( !$uid){
$uid = Yii::$app->user->identity->id;
}
if( !$this->privilege_urls ){
$role_ids = UserRole::find()->where([ 'uid' => $uid ])->select('role_id')->asArray()->column();
if( $role_ids ){
//在通过角色 取出 所属 权限关系
$permission_ids = RolePermission::find()->where([ 'role_id' => $role_ids ])->select('permission_id')->asArray()->column();
//在权限表中取出所有的权限链接
$list = Permission::find()->where([ 'id' => $permission_ids ])->all();
$urls = [];
if( $list ){
foreach( $list as $_item ){
$tmp_urls = @json_decode( $_item['urls'],true );
$urls[] = $tmp_urls;
}
$this->privilege_urls = array_merge( $this->privilege_urls,$urls );
}
}
}
return $this->privilege_urls ;
}
//验证登录是否有效,返回 true or false
protected function checkLoginStatus(){
if (Yii::$app->user->isGuest){
return false;
}
return true;
}
public function request($key, $def = false) {
$result = $def;
if(isset($key)) {
$request = Yii::$app->getRequest();
if($request->isGet) {
$result = $request->get($key, $def);
}else if($request->isPost) {
$result = $request->post($key, $def);
}
}
return $result;
}
}
整体代码没有准备,如有需要可以留言,准备后会将链接写在这里。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。