Yii2的RBAC实现

一、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;
    }
}

整体代码没有准备,如有需要可以留言,准备后会将链接写在这里。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值