Yii2的RBAC實現

1、RBAC的前期準備php

RBAC的權限管理須要知道怎麼給用戶分配角色,給角色分配權限,以權限來精細化須要的操做,判斷是否有權限來操做這一步,達到管理權限的目的。
先展現下要達到的效果 :

圖片描述

左邊爲三劍客:用戶、角色、權限;
下面爲測試頁面
2、RBAC的數據表
用戶表用的是yii2-admin中migration裏面的用戶表web

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='角色權限表';

3、視圖渲染部分。
將準備好的頁面放到views層,在models層創建和表相對應的文件
文件結構:
models層:
圖片描述數據庫

view層:
圖片描述json

4、邏輯部分:
以角色的增長和修改、給角色增長權限爲例:
一、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]);
    }
   /*
    *方法講解:
    *編輯+新增
    *一、若是能獲取到角色的id。則從數據庫取出數據來渲染。若沒有則渲染不顯示數據
    *二、提交更新相應的修改或者增長。一樣以id來區分
    *三、新增方法簡化還可用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
        ]);

    }
}

二、因爲基礎的增長改查都會編寫。在這裏寫set模板渲染
set.php在view中的role目錄,代碼以下:yii2

<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>

給三劍客增長、修改、刪除的核心代碼已經結束
5、基礎控制器的編寫:
實現辨別是否有權限訪問要訪問的頁面
BaseControllerapp

<?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;
    }
}

總體代碼沒有準備,若有須要能夠留言,準備後會將連接寫在這裏。框架

相關文章
相關標籤/搜索