溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

ThinkPHP中RBAC是什么

發(fā)布時(shí)間:2020-10-30 10:04:29 來(lái)源:億速云 閱讀:150 作者:小新 欄目:編程語(yǔ)言

小編給大家分享一下ThinkPHP中RBAC是什么,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

RBAC是什么,能解決什么難題?

RBAC是Role-Based Access Control的首字母,譯成中文即基于角色的權(quán)限訪問(wèn)控制,說(shuō)白了也就是用戶(hù)通過(guò)角色與權(quán)限進(jìn)行關(guān)聯(lián)[其架構(gòu)靈感來(lái)源于操作系統(tǒng)的GBAC(GROUP-Based Access Control)的權(quán)限管理控制]。簡(jiǎn)單的來(lái)說(shuō),一個(gè)用戶(hù)可以擁有若干角色,每一個(gè)角色擁有若干權(quán)限。這樣,就構(gòu)造成“用戶(hù)-角色-權(quán)限”的授權(quán)模型。在這種模型中,用戶(hù)與角色之間,角色與權(quán)限之間,一般者是多對(duì)多的關(guān)系。其對(duì)應(yīng)關(guān)系如下:

ThinkPHP中RBAC是什么

在許多的實(shí)際應(yīng)用中,系統(tǒng)不只是需要用戶(hù)完成簡(jiǎn)單的注冊(cè),還需要對(duì)不同級(jí)別的用戶(hù)對(duì)不同資源的訪問(wèn)具有不同的操作權(quán)限。且在企業(yè)開(kāi)發(fā)中,權(quán)限管理系統(tǒng)也成了重復(fù)開(kāi)發(fā)效率最高的一個(gè)模塊之一。而在多套系統(tǒng)中,對(duì)應(yīng)的權(quán)限管理只能滿足自身系統(tǒng)的管理需要,無(wú)論是在數(shù)據(jù)庫(kù)設(shè)計(jì)、權(quán)限訪問(wèn)和權(quán)限管理機(jī)制方式上都可能不同,這種不致性也就存在如下的憋端:

? 維護(hù)多套系統(tǒng),重復(fù)造輪子,時(shí)間沒(méi)用在刀刃上
? 用戶(hù)管理、組織機(jī)制等數(shù)據(jù)重復(fù)維護(hù),數(shù)據(jù)的完整性、一致性很難得到保障
? 權(quán)限系統(tǒng)設(shè)計(jì)不同,概念理解不同,及相應(yīng)技術(shù)差異,系統(tǒng)之間集成存在問(wèn)題,單點(diǎn)登錄難度大,也復(fù)雜的企業(yè)系統(tǒng)帶來(lái)困難

RBAC是基于不斷實(shí)踐之后,提出的一個(gè)比較成熟的訪問(wèn)控制方案。實(shí)踐表明,采用基于RBAC模型的權(quán)限管理系統(tǒng)具有以下優(yōu)勢(shì):由于角色、權(quán)限之間的變化比角色、用戶(hù)關(guān)系之間的變化相對(duì)要慢得多,減小了授權(quán)管理的復(fù)雜性,降低管理開(kāi)銷(xiāo);而且能夠靈活地支持應(yīng)用系統(tǒng)的安全策略,并對(duì)應(yīng)用系統(tǒng)的變化有很大的伸縮性;在操作上,權(quán)限分配直觀、容易理解,便于使用;分級(jí)權(quán)限適合分層的用戶(hù)級(jí)形式;重用性強(qiáng)。

ThinkPHP中RBAC實(shí)現(xiàn)體系

ThinkPHP中RBAC基于Java的Spring的Acegi安全系統(tǒng)作為參考原型,并做了相應(yīng)的簡(jiǎn)化處理,以適應(yīng)當(dāng)前的ThinkPHP結(jié)構(gòu),提供一個(gè)多層、可定制的安全體系來(lái)為應(yīng)用開(kāi)發(fā)提供安全控制。安全體系中主要有以下幾部分:

? 安全攔截器

? 認(rèn)證管理器

? 決策訪問(wèn)管理器

? 運(yùn)行身份管理器

安全攔截器

安全攔截器就好比一道道門(mén),在系統(tǒng)的安全防護(hù)系統(tǒng)中可能存在很多不同的安全控制環(huán)節(jié),一旦某個(gè)環(huán)節(jié)你未通過(guò)安全體系認(rèn)證,那么安全攔截器就會(huì)實(shí)施攔截。

認(rèn)證管理器

防護(hù)體系的第一道門(mén)就是認(rèn)證管理器,認(rèn)證管理器負(fù)責(zé)決定你是誰(shuí),一般它通過(guò)驗(yàn)證你的主體(通常是一個(gè)用戶(hù)名)和你的憑證(通常是一個(gè)密碼),或者更多的資料來(lái)做到。更簡(jiǎn)單的說(shuō),認(rèn)證管理器驗(yàn)證你的身份是否在安全防護(hù)體系授權(quán)范圍之內(nèi)。

訪問(wèn)決策管理

雖然通過(guò)了認(rèn)證管理器的身份驗(yàn)證,但是并不代表你可以在系統(tǒng)里面肆意妄為,因?yàn)槟氵€需要通過(guò)訪問(wèn)決策管理這道門(mén)。訪問(wèn)決策管理器對(duì)用戶(hù)進(jìn)行授權(quán),通過(guò)考慮你的身份認(rèn)證信息和與受保護(hù)資源關(guān)聯(lián)的安全屬性決定是是否可以進(jìn)入系統(tǒng)的某個(gè)模塊,和進(jìn)行某項(xiàng)操作。例如,安全規(guī)則規(guī)定只有主管才允許訪問(wèn)某個(gè)模塊,而你并沒(méi)有被授予主管權(quán)限,那么安全攔截器會(huì)攔截你的訪問(wèn)操作。
決策訪問(wèn)管理器不能單獨(dú)運(yùn)行,必須首先依賴(lài)認(rèn)證管理器進(jìn)行身份確認(rèn),因此,在加載訪問(wèn)決策過(guò)濾器的時(shí)候已經(jīng)包含了認(rèn)證管理器和決策訪問(wèn)管理器。

為了滿足應(yīng)用的不同需要,ThinkPHP 在進(jìn)行訪問(wèn)決策管理的時(shí)候采用兩種模式:登錄模式和即時(shí)模式。登錄模式,系統(tǒng)在用戶(hù)登錄的時(shí)候讀取改用戶(hù)所具備的授權(quán)信息到 Session,下次不再重新獲取授權(quán)信息。也就是說(shuō)即使管理員對(duì)該用戶(hù)進(jìn)行了權(quán)限修改,用戶(hù)也必須在下次登錄后才能生效。即時(shí)模式就是為了解決上面的問(wèn)題,在每次訪問(wèn)系統(tǒng)的模塊或者操作時(shí)候,進(jìn)行即使驗(yàn)證該用戶(hù)是否具有該模塊和操作的授權(quán),從更高程度上保障了系統(tǒng)的安全。

運(yùn)行身份管理器

運(yùn)行身份管理器的用處在大多數(shù)應(yīng)用系統(tǒng)中是有限的,例如某個(gè)操作和模塊需要多個(gè)身份的安全需求,運(yùn)行身份管理器可以用另一個(gè)身份替換你目前的身份,從而允許你訪問(wèn)應(yīng)用系統(tǒng)內(nèi)部更深處的受保護(hù)對(duì)象。這一層安全體系目前的 RBAC 中尚未實(shí)現(xiàn)。

ThinkPHP中RBAC認(rèn)證流程

對(duì)應(yīng)上面的安全體系,ThinkPHP 的 RBAC 認(rèn)證的過(guò)程大致如下:

1、判斷當(dāng)前模塊的當(dāng)前操作是否需要認(rèn)證

2、如果需要認(rèn)證并且尚未登錄,跳到認(rèn)證網(wǎng)關(guān),如果已經(jīng)登錄 執(zhí)行5

3、通過(guò)委托認(rèn)證進(jìn)行用戶(hù)身份認(rèn)證

4、獲取用戶(hù)的決策訪問(wèn)列表

5、判斷當(dāng)前用戶(hù)是否具有訪問(wèn)權(quán)限

權(quán)限管理的具體實(shí)現(xiàn)過(guò)程

RBAC相關(guān)的數(shù)據(jù)庫(kù)介紹

在ThinkPHP完整包,包含了RBAC處理類(lèi)RBAC.class.php文件,位于Extend/Library/ORG/Util。打開(kāi)該文件,其中就包含了使用RBAC必備的4張表,SQL語(yǔ)句如下(復(fù)制后請(qǐng)?zhí)鎿Q表前綴):

CREATE TABLE IF NOT EXISTS `think_access` (
  `role_id` smallint(6) unsigned NOT NULL,
  `node_id` smallint(6) unsigned NOT NULL,
  `level` tinyint(1) NOT NULL,
  `module` varchar(50) DEFAULT NULL,  KEY `groupId` (`role_id`),  KEY `nodeId` (`node_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;CREATE TABLE IF NOT EXISTS `think_node` (
  `id` smallint(6) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL,
  `title` varchar(50) DEFAULT NULL,
  `status` tinyint(1) DEFAULT '0',
  `remark` varchar(255) DEFAULT NULL,
  `sort` smallint(6) unsigned DEFAULT NULL,
  `pid` smallint(6) unsigned NOT NULL,
  `level` tinyint(1) unsigned NOT NULL,  PRIMARY KEY (`id`),  KEY `level` (`level`),  KEY `pid` (`pid`),  KEY `status` (`status`),  KEY `name` (`name`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8;CREATE TABLE IF NOT EXISTS `think_role` (
  `id` smallint(6) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL,
  `pid` smallint(6) DEFAULT NULL,
  `status` tinyint(1) unsigned DEFAULT NULL,
  `remark` varchar(255) DEFAULT NULL,  PRIMARY KEY (`id`),  KEY `pid` (`pid`),  KEY `status` (`status`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 ;CREATE TABLE IF NOT EXISTS `think_role_user` (
  `role_id` mediumint(9) unsigned DEFAULT NULL,
  `user_id` char(32) DEFAULT NULL,  KEY `group_id` (`role_id`),  KEY `user_id` (`user_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

下面對(duì)RBAC相關(guān)的數(shù)據(jù)庫(kù)表及字段作一下介紹:

表名字段名字段類(lèi)型作用
ly_useridINT用戶(hù)ID(唯一識(shí)別號(hào))
usernameVARCHAR(16)用戶(hù)名
passwordVARCHAR(32)密碼
emailVARCHAR(100)用戶(hù)郵箱
create_timeTIMESTAMP創(chuàng)建時(shí)間(時(shí)間戳)
logintimeTIMESTAMP最近一次登錄時(shí)間(時(shí)間戳)
loginipVARCHAR(15)最近登錄的IP地址
statusTINYINT(1)啟用狀態(tài):0:表示禁用;1:表示啟用
remarkVARCHAR(255)備注信息
ly_roleidINT角色I(xiàn)D
nameVARCHAR(20)角色名稱(chēng)
pidSMALLINT(6)父角色對(duì)應(yīng)ID
statusTINYINT(1)啟用狀態(tài)(同上)
remarkVARCHAR(255)備注信息
ly_nodeidSMALLINT(6)節(jié)點(diǎn)ID
nameVARCHAR(20)節(jié)點(diǎn)名稱(chēng)(英文名,對(duì)應(yīng)應(yīng)用控制器、應(yīng)用、方法名)
titleVARCHAR(50)節(jié)點(diǎn)中文名(方便看懂)
statusTINYINT(1)啟用狀態(tài)(同上)
remarkVARCHAR(255)備注信息
sortSMALLINT(6)排序值(默認(rèn)值為50)
pidSMALLINT(6)父節(jié)點(diǎn)ID(如:方法pid對(duì)應(yīng)相應(yīng)的控制器)
levelTINYINT(1)節(jié)點(diǎn)類(lèi)型:1:表示應(yīng)用(模塊);2:表示控制器;3:表示方法
ly_role_useruser_idINT用戶(hù)ID
role_idSMALLINT(6)角色I(xiàn)D
ly_accessrole_idSMALLINT(6)角色I(xiàn)D
node_idSMALLINT(6)節(jié)點(diǎn)ID
level
module

以下是數(shù)據(jù)庫(kù)表各字段的關(guān)聯(lián)關(guān)系:

ThinkPHP中RBAC是什么

實(shí)現(xiàn)RBAC管理的前導(dǎo)性工作

基于ThinkPHP實(shí)現(xiàn)RBAC的權(quán)限管理系統(tǒng)中,首先要做一些前導(dǎo)性的工作(系統(tǒng)數(shù)據(jù)庫(kù)設(shè)計(jì)TP已經(jīng)為我們完成了),主要分以下幾個(gè)方面:

? 用戶(hù)(增、刪、改、查)
? 角色(增、刪、改、查)
? 節(jié)點(diǎn)(增、刪、改、查)
? 配置權(quán)限(更新權(quán)限)

具體實(shí)現(xiàn)的代碼如下(相關(guān)解釋均在注釋之中):

<?php
/**
 *
 */
namespace Home\Controller;

use Home\Controller\BaseController;
use Home\Model\AdminUserModel;
use Org\Util\Tree;
use Think\Page;

class RbacController extends BaseController
{
    //初始化操作
    public function _initialize()
    {
        if (!IS_AJAX) $this->error('你訪問(wèn)的頁(yè)面不存在,請(qǐng)稍后再試');
    }

    public function userIndex()
    {
        if (IS_POST) {
            $condition['username'] = array('like', "%" . trim(I('keybord')) . "%");
            $model = D('AdminUser');
            $count = $model->where($condition)->count();
            $Page = new Page($count, 3);
            $Page->setConfig('theme', '%FIRST% %UP_PAGE% %LINK_PAGE% %DOWN_PAGE% %END%');
            $show = $Page->show();
            //select search
            $list = $model->where($condition)->field('password', true)->relation(true)->limit($Page->firstRow . ',' . $Page->listRows)->select();
            $this->show = $show;
            $this->list = $list;
            $this->display('AdminUser/index');
        } else {
            $model = D('AdminUser');
            $count = $model->count();
            $Page = new Page($count, 6);
            $Page->setConfig('header', '共%TOTAL_ROW%條');
            $Page->setConfig('first', '首頁(yè)');
            $Page->setConfig('last', '共%TOTAL_PAGE%頁(yè)');
            $Page->setConfig('prev', '上一頁(yè)');
            $Page->setConfig('next', '下一頁(yè)');
            $Page->setConfig('link', 'indexpagenumb');
            $Page->setConfig('theme', '%FIRST% %UP_PAGE% %LINK_PAGE% %DOWN_PAGE% %END%');
            $show = $Page->show();
            //select search
            $list = $model->field('password', true)->relation(true)->limit($Page->firstRow . ',' . $Page->listRows)->select();
            $this->show = $show;
            $this->list = $list;
            $this->display('Rbac/userIndex');
        }

    }

    /*
    *  平臺(tái)用戶(hù)異步驗(yàn)證
    */
    public function checkUser()
    {
        $username = trim(I('username'));
        $conditions = array('username' => ':username');
        $result = M('AdminUser')->where($conditions)->bind(':username', $username)->find();
        //如果不存在,則可以創(chuàng)建,也就是返回的是true
        if (!$result) {
            echo 'true';
        } else {
            echo 'false';
        }
        exit();
    }

    /*
     *  創(chuàng)建平臺(tái)用戶(hù),這里開(kāi)啟了服務(wù)器驗(yàn)證
     */
    public function createAdminUser()
    {
        if (IS_POST) {
            /**
             * [實(shí)例化User對(duì)象]
             * D方法實(shí)例化模型類(lèi)的時(shí)候通常是實(shí)例化某個(gè)具體的模型類(lèi),如果你僅僅是對(duì)數(shù)據(jù)表進(jìn)行基本的CURD操作的話,
             * 使用M方法實(shí)例化的話,由于不需要加載具體的模型類(lèi),所以性能會(huì)更高。
             */
            $model = D('AdminUser');
            /**
             * 如果創(chuàng)建失敗 表示驗(yàn)證沒(méi)有通過(guò) 輸出錯(cuò)誤提示信息
             * $model->create() 會(huì)自動(dòng)調(diào)用驗(yàn)證規(guī)則
             */
            if (!$model->create()) return $this->error($model->getError());
            //$username = $model->username;
            //$model->add() 插入數(shù)據(jù)后會(huì)自動(dòng)的摧毀數(shù)據(jù)
            if (!$uid = $model->add()) return $this->success('注冊(cè)失敗', U('Rbac/userIndex'));//獲取用戶(hù)id
            // 如果是注冊(cè)用戶(hù)的話
//            session('uid', $uid);
//            session('username', $username);
            //用戶(hù)添加成功后,給用戶(hù)角色表添加數(shù)據(jù)
            $role['role_id'] = I('role_id');
            $role['user_id'] = $uid;
            //添加該管理員操作到操作日志中
            $desc = '給ID為:[' . $_POST['role_id'] . ']的角色,新增用戶(hù):[' . $_POST['username'] . '],密碼為:[' . $_POST['password'] . ']' . '其他參數(shù)' . $_POST;
            addOperationLog($desc);
            if (D('AdminRoleUser')->add($role)) {
                return $this->success('添加平臺(tái)用戶(hù)成功', U('Rbac/userIndex'));
            } else {
                return $this->error('添加平臺(tái)用戶(hù)失敗', U('Rbac/userIndex'));
            }
            return $this->success('添加平臺(tái)用戶(hù)成功', U('Rbac/userIndex'));
        }
        $this->role_list = M('AdminRole')->select();
        $this->display('Rbac/createAdminUser');
    }

    /**
     * 改變用戶(hù)角色
     * 可以支持不執(zhí)行SQL而只是返回SQL語(yǔ)句:$User->fetchSql(true)->add($data);
     */
    public function updateUser()
    {
        $userId = I('get.id');
        if (IS_POST) {
            $data['user_id'] = I('post.user_id');
            $data['role_id'] = I('post.role_id');
            $model = M('AdminRoleUser');
            if ($model->where(array('user_id' => $data['user_id']))->delete() == false) {
                return $this->error('用戶(hù)角色修改失敗', U('Rbac/updateUser', array('id' => $userId)));
            }
            if ($model->add($data) == false) {
                return $this->error('用戶(hù)角色修改失敗', U('Rbac/updateUser', array('id' => $userId)));
            }
            return $this->success('用戶(hù)角色修改成功', U('Rbac/userIndex'));
        }
        $this->role_list = M('AdminRole')->select();
        $this->user = M('AdminUser')->join('tour_admin_role_user ON tour_admin_role_user.user_id = tour_admin_user.id')->where(array('id' => $userId))->field('user_id,username,role_id')->find();
        $this->display();
    }

    //刪除用戶(hù)
    public function delUser()
    {
        $user_id = I('post.id', '', 'int');
        $user = D('AdminUser');
        $result = $user->relation(true)->where(array('id' => $user_id))->delete();
        if ($result) {
            //添加該管理員操作到操作日志中
            $desc = '刪除用戶(hù)ID:' . $user_id . '成功';
            addOperationLog($desc);
            $response = ['status' => 200, 'errmsg' => '刪除成功', 'dataList' => $result];
            return $this->ajaxReturn($response, 'JSON');
        }
        //添加該管理員操作到操作日志中
        $desc = '刪除用戶(hù)ID:' . $user_id . '失敗';
        addOperationLog($desc);
        $response = ['status' => 500, 'errmsg' => '刪除失敗', 'dataList' => $result];
        return $this->ajaxReturn($response, 'JSON');

    }

    //設(shè)置用戶(hù)狀態(tài)
    public function userStatus()
    {
        $uid = I('post.id');
        $db = M('AdminUser');
        $status = $db->where(array('id' => $uid))->getField('status');
        $status = ($status == 1) ? 0 : 1;
        if ($db->where(array('id' => $uid))->setField('status', $status)) {
            $response = ['status' => 200, 'errmsg' => '改變成功', 'dataList' => $status];
            return $this->ajaxReturn($response, 'JSON');
        }
        //添加該管理員操作到操作日志中
        $desc = '設(shè)置用戶(hù)狀態(tài):' . $uid . '失敗';
        addOperationLog($desc);
        $response = ['status' => 500, 'errmsg' => '改變失敗', 'dataList' => $status];
        return $this->ajaxReturn($response, 'JSON');
    }

    /***********************************節(jié)點(diǎn)開(kāi)始****************************************************/
    public function nodeIndex()
    {
        $db = M('AdminNode');
        $node = $db->order('id')->select();
        $this->nodelist = Tree::create($node);
        $this->display('Rbac/nodeIndex');
    }

    //創(chuàng)建權(quán)限表單處理
    public function createNode()
    {
        $db = M('AdminNode');

        //創(chuàng)建權(quán)限表單處理
        if (IS_POST) {
            $db->create();
            if (!$db->add()) {
                return $this->error("權(quán)限添加失敗", U('Rbac/nodeIndex'));
            }
            return $this->success('權(quán)限添加成功', U('Rbac/nodeIndex'));
        }
        $node = $db->where('level !=3')->order('sort')->select();
        $this->nodelist = Tree::create($node);
        $this->display();
    }

    /*
    *   刪除權(quán)限
    */
    public function delNode()
    {
        $result = M('AdminNode')->where(array('id' => I('post.id', '', 'int')))->delete();
        if ($result) {
            $response = ['status' => 200, 'errmsg' => '刪除成功', 'dataList' => $result];
            return $this->ajaxReturn($response, 'JSON');
        }
        $response = ['status' => 500, 'errmsg' => '刪除失敗', 'dataList' => $result];
        return $this->ajaxReturn($response, 'JSON');
    }

    /*
    *   設(shè)置權(quán)限狀態(tài)
    */
    public function NodeStatus()
    {
        $id = I('post.id');
        $db = M('AdminNode');
        $status = $db->where(array('id' => $id))->getField('status');

        $status = ($status == 1) ? 0 : 1;
        if ($db->where(array('id' => $id))->setField('status', $status)) {
            $response = ['status' => 200, 'errmsg' => '修改成功', 'dataList' => $status];
            return $this->ajaxReturn($response, 'JSON');
        }
        $response = ['status' => 500, 'errmsg' => '修改失敗', 'dataList' => $status];
        return $this->ajaxReturn($response, 'JSON');
    }

    /*
   *   該節(jié)點(diǎn)是否在菜單欄顯示
   */
    public function showMenus()
    {
        $id = I('post.id');
        $db = M('AdminNode');
        $show = $db->where(array('id' => $id))->getField('menus');
        $menus = ($show == 1) ? 0 : 1;
        $result = $db->where(array('id' => $id))->setField('menus', $menus);
        if ($result) {
            $response = ['status' => 200, 'errmsg' => '修改成功', 'dataList' => $result];
            return $this->ajaxReturn($response, 'JSON');
        }
        $response = ['status' => 500, 'errmsg' => '修改失敗', 'dataList' => $result];
        return $this->ajaxReturn($response, 'JSON');
    }

    /***********************************角色開(kāi)始****************************************************/
    public function roleIndex()
    {
        $db = M('AdminRole');
        $this->rolelist = $db->select();
        $this->display();
    }

    /*
     *創(chuàng)建角色
     */
    public function createAdminRole()
    {
        if (IS_POST) {
            $name = I('post.name', '', 'strip_tags');
            $remark = I('post.remark', '', 'strip_tags');
            $pid = I('post.pid', '', 'strip_tags');  // 用strip_tags過(guò)濾$_GET['title']
            if (empty($name)) return $this->error('角色名稱(chēng)不能為空');
            $role = M('AdminRole');
            $where['name'] = ':name';
            $roleName = $role->where($where)->bind(':name', $name, \PDO::PARAM_STR)->getField('name');
            if ($roleName) return $this->error("角色名稱(chēng):'" . $name . "'已經(jīng)存在", U('Rbac/roleIndex'));

            $role->name = $name;
            $role->remark = $remark;
            $role->pid = $pid;
            //create方法并不算是連貫操作,因?yàn)槠浞祷刂悼赡苁遣紶栔?,所以必須要進(jìn)行嚴(yán)格判斷。
            if ($role->create()) {
                // 如果主鍵是自動(dòng)增長(zhǎng)型 成功后返回值就是最新插入的值
                $result = $role->field('name,remark,pid')->add();
                //如果在add方法之前調(diào)用field方法,則表示只允許寫(xiě)入指定的字段數(shù)據(jù),其他非法字段將會(huì)被過(guò)濾
                if (!$result) return $this->error("角色添加失敗", U('Rbac/createpartent'));
                return $this->success('角色添加成功', U('Rbac/roleIndex'));
            }
            return $this->success('角色添加成功', U('Rbac/roleIndex'));
        }
        $this->display();
    }

    /*
     *添加權(quán)限Node位權(quán)限表,Access為權(quán)限-角色關(guān)聯(lián)表
     */
    public function addNode()
    {
        $rid = I('rid', '', 'int');
        if (!is_numeric($rid)) return $this->success('參數(shù)類(lèi)型錯(cuò)誤,必須是數(shù)字', U('Rbac/roleIndex'));
        //getFieldById針對(duì)某個(gè)字段(ID)查詢(xún)并返回某個(gè)字段(name)的值
        $roleModel = M('AdminRole');
        $roleWhere['id'] = ':id';
        $role_name = $roleModel->where($roleWhere)->bind(':id', $rid, \PDO::PARAM_INT)->getField('name');
        if ($role_name == false) return $this->success('沒(méi)有找到該角色', U('Rbac/roleIndex'));

        //根據(jù)角色遍歷所有權(quán)限
        $access = M('AdminAccess');
        if (IS_POST) {
            $actions = I('post.actions');
            try {
                $access->startTrans();
                $where['role_id'] = ':role_id';
                $mod1 = $access->where($where)->bind(':role_id', $rid)->delete();
                if (!$mod1) $access->rollback();
                $data = array();
                foreach ($actions as $value) {
                    $tmp = explode('_', $value);
                    $data[] = array(
                        'role_id' => $rid,
                        'node_id' => $tmp[0],
                        'level' => $tmp[1]
                    );
                }
                if (!($access->addAll($data))) {
                    $access->rollback();
                } else {
                    $access->commit();
                }
                return $this->success('權(quán)限設(shè)置成功', U('Rbac/addNode', array('rid' => $rid)));
            } catch (\Exception $e) {
                $access->rollback();
                return $this->success('權(quán)限設(shè)置異常', U('Rbac/addNode', array('rid' => $rid)));
            }

        }
        $node = M('AdminNode')->order('id')->select();
        $node_list = Tree::create($node);
        $node_arr = array();
        foreach ($node_list as $value) {
            $conditions['node_id'] = $value['id'];
            $conditions['role_id'] = $rid;
            $count = $access->where($conditions)->count();
            if ($count) {
                $value['access'] = '1';
            } else {
                $value['access'] = '0';
            }
            $node_arr[] = $value;
        }
        $this->role_name = $role_name;
        $this->node_list = $node_arr;
        $this->rid = $rid;
        $this->display();
    }

    /*
     *刪除角色以及角色所擁有的權(quán)限
     */
    public function delRole()
    {
        $role_id = I('post.role_id', '', 'int');
        $user = D('AdminRole');
        $result = $user->relation(true)->where(array('id' => $role_id))->delete();
        if ($result) {
            $response = ['status' => 200, 'errmsg' => '修改成功', 'dataList' => $result];
            return $this->ajaxReturn($response, 'JSON');
        }
        $response = ['status' => 500, 'errmsg' => '修改失敗', 'dataList' => $result];
        return $this->ajaxReturn($response, 'JSON');
    }

    /*
    *   設(shè)置角色狀態(tài)
    */
    public function roleStatus()
    {
        $rid = I('post.rid');
        $db = M('AdminRole');
        $status = $db->where(array('id' => $rid))->getField('status');
        $status = ($status == 1) ? 0 : 1;
        if ($db->where(array('id' => $rid))->setField('status', $status)) {
            $response = ['status' => 200, 'errmsg' => '修改成功', 'dataList' => $status];
            return $this->ajaxReturn($response, 'JSON');
        }
        $response = ['status' => 500, 'errmsg' => '修改失敗', 'dataList' => $status];
        return $this->ajaxReturn($response, 'JSON');
    }
}

ThinkPHP中RBAC類(lèi)的詳解

在ThinkPHP處理權(quán)限管理中,真正的難點(diǎn)并不是在RBAC類(lèi)的使用上,上面相關(guān)的處理(權(quán)限配置,節(jié)點(diǎn)管理等);所以當(dāng)完成以上工作,其實(shí)RBAC系統(tǒng)已經(jīng)完成了90%。下面先從ThinkPHP中RBAC的配置說(shuō)起(詳細(xì)請(qǐng)參看對(duì)應(yīng)的注釋內(nèi)容):

<?php

 
return array(
 
"USER_AUTH_ON" => true, //是否開(kāi)啟權(quán)限驗(yàn)證(必配)
"USER_AUTH_TYPE" => 1, //驗(yàn)證方式(1、登錄驗(yàn)證;2、實(shí)時(shí)驗(yàn)證)
 
"USER_AUTH_KEY" => 'uid', //用戶(hù)認(rèn)證識(shí)別號(hào)(必配)
"ADMIN_AUTH_KEY" => 'superadmin', //超級(jí)管理員識(shí)別號(hào)(必配)
"USER_AUTH_MODEL" => 'user', //驗(yàn)證用戶(hù)表模型 ly_user
'USER_AUTH_GATEWAY' => '/Public/login', //用戶(hù)認(rèn)證失敗,跳轉(zhuǎn)URL
 
'AUTH_PWD_ENCODER'=>'md5', //默認(rèn)密碼加密方式
 
"RBAC_SUPERADMIN" => 'admin', //超級(jí)管理員名稱(chēng)
 
 
"NOT_AUTH_MODULE" => 'Index,Public', //無(wú)需認(rèn)證的控制器
"NOT_AUTH_ACTION" => 'index', //無(wú)需認(rèn)證的方法
 
'REQUIRE_AUTH_MODULE' => '', //默認(rèn)需要認(rèn)證的模塊
'REQUIRE_AUTH_ACTION' => '', //默認(rèn)需要認(rèn)證的動(dòng)作
 
'GUEST_AUTH_ON' => false, //是否開(kāi)啟游客授權(quán)訪問(wèn)
'GUEST_AUTH_ID' => 0, //游客標(biāo)記
 
"RBAC_ROLE_TABLE" => 'ly_role', //角色表名稱(chēng)(必配)
"RBAC_USER_TABLE" => 'ly_role_user', //用戶(hù)角色中間表名稱(chēng)(必配)
"RBAC_ACCESS_TABLE" => 'ly_access', //權(quán)限表名稱(chēng)(必配)
"RBAC_NODE_TABLE" => 'ly_node', //節(jié)點(diǎn)表名稱(chēng)(必配)
);

注意:

? 以上有的配置項(xiàng)并非必須的,但標(biāo)有“必配”,則必須配置

? 無(wú)需認(rèn)證的權(quán)限和方法與需要認(rèn)證的模塊和動(dòng)作可以根據(jù)需要選擇性配置,還有部分權(quán)限相關(guān)配置并未列表出

RBAC處理類(lèi)提供靜態(tài)的方法

ThinkPHP的RBAC處理類(lèi)提供給我們很多相關(guān)的靜態(tài)方法如下:

方法名接收參數(shù)說(shuō)明返回值說(shuō)明
RBAC::authenticate(map,map,model='');

$map:ThinkPHP數(shù)據(jù)庫(kù)處理類(lèi)的where條件參

數(shù)$model:用戶(hù)表名,默認(rèn)取配置文件C('USER_AUTH_MODEL')

array

RBAC::authenticate(array("username"=>"admin","userpwd" => I('POST.pwd','', 'md5')));

返回值是在用戶(hù)表中,以$map為條件where的查閱結(jié)果集

0RBAC::saveAccessList($authId=null);$authId:用戶(hù)識(shí)別號(hào),默認(rèn)取C('USER_AUTH_KEY');返回一個(gè)空值如果驗(yàn)證方式為登錄驗(yàn)證,則將權(quán)限寫(xiě)入session中,否則不作任何處理
RBAC::getRecordAccessList(authId=null,authId=null,module='');

$authId:用戶(hù)識(shí)別號(hào)(可不傳)

$module:當(dāng)前操作的模塊名稱(chēng)

Array返回一個(gè)包含權(quán)限的ID的數(shù)組
RBAC::checkAccess()無(wú)返回true或false檢查當(dāng)前操作是否需要認(rèn)證(根據(jù)配置中需要認(rèn)證和不需要評(píng)論的模塊或方法得出)
RBAC::checkLogin()無(wú)true如果當(dāng)前操作需要認(rèn)證且用戶(hù)沒(méi)有登錄,繼續(xù)檢測(cè)是否開(kāi)啟游客授權(quán)。如果開(kāi)啟游客授權(quán),則寫(xiě)入游客權(quán)限;否則跳到登錄頁(yè)
RBAC::AccessDecision($appName=APP_NAME)$appName:選傳,有默認(rèn)值

true:表示有操作權(quán)限

false:無(wú)操作權(quán)限

AccessDecision(appName=APPNAME)方法,檢測(cè)當(dāng)前項(xiàng)目模塊操作,是否存在于appName=APPNAME)方法,檢測(cè)當(dāng)前項(xiàng)目模塊操作,是否存在于_SESSION['_ACCESS_LIST']數(shù)組中$_SESSION['_ACCESS_LIST']['當(dāng)前操作']['當(dāng)前模塊']['當(dāng)前操作']是否存在。如果存在表示有權(quán)限,返回true;否則返回flase。
RBAC::getAccessList($authId)$authId:用戶(hù)識(shí)別號(hào)(選傳,程序自動(dòng)獲取)Array通過(guò)數(shù)據(jù)庫(kù)查詢(xún)?nèi)〉卯?dāng)前認(rèn)證號(hào)的所有權(quán)限列表
RBAC::getModuleAccessList(authId,authId,module)$authId:用戶(hù)識(shí)別號(hào)(必)$module:對(duì)應(yīng)的模塊(必)Array返回指定用戶(hù)可訪問(wèn)的節(jié)點(diǎn)權(quán)限數(shù)組

注意:在使用RBAC::AccessDecision()方法時(shí),如果你開(kāi)啟了項(xiàng)目分組,則必須傳入當(dāng)前分組,代碼如下:

//權(quán)限驗(yàn)證
if(C('USER_AUTH_ON') && !$notAuth) {
import('ORG.Util.RBAC');
//使用了項(xiàng)目分組,則必須引入GROUP_NAME
RBAC::AccessDecision(GROUP_NAME) || $this->error("你沒(méi)有對(duì)應(yīng)的權(quán)限");
}

RBAC處理類(lèi)的實(shí)際應(yīng)用

在完成用戶(hù)登錄,角色創(chuàng)建,節(jié)點(diǎn)增刪改查的工作后,就只剩下了RBAC如何在對(duì)應(yīng)程序代碼中應(yīng)用了。挻簡(jiǎn)單的,只用在原來(lái)的代碼其他上改動(dòng)幾個(gè)地方即可。

? 用戶(hù)登錄時(shí),寫(xiě)入用戶(hù)權(quán)限
? 用戶(hù)操作時(shí),進(jìn)行權(quán)限驗(yàn)證

下面是用戶(hù)登錄時(shí)的實(shí)現(xiàn)代碼:

<?php
namespace Home\Controller;

use Think\Controller;
use Org\Util\Rbac;

class LoginController extends Controller
{
    public function index()
    {
        $this->display();
    }

    /*
     * 異步驗(yàn)證賬號(hào)
    */
    public function checkUser()
    {
        $username = I('username');
        $conditions = array('username' => ':username');
        $result = M('User')->where($conditions)->bind(':username', $username)->find();
        //如果不存在,則可以創(chuàng)建,也就是返回的是true
        if (!$result) {
            echo 'false';
        } else {
            echo 'true';
        }
        exit();
    }

    /*
     * 異步驗(yàn)證密碼
    */
    public function checkPwd()
    {
        $username = I('post.username');
        $password = I('post.password');
        $conditions = array('username' => ':username');
        $result = M('User')->where($conditions)->bind(':username', $username)->find();
        if (md5($password) != $result['password']) {
            echo 'false';
        } else {
            echo 'true';
        }
        exit();
    }

    /*
     * 檢查登錄
    */
    public function checkLogin()
    {
        if (!IS_POST) $this->error('非法訪問(wèn)');

        // 采用htmlspecialchars方法對(duì)$_GET['name'] 進(jìn)行過(guò)濾,如果不存在則返回空字符串
        $username = I('post.username', '', 'htmlspecialchars');
        // 采用正則表達(dá)式進(jìn)行變量過(guò)濾,如果正則匹配不通過(guò)的話,則返回默認(rèn)值。
        //I('get.name','','/^[A-Za-z]+$/');
        $password = md5(I('post.password'));

        $user = D('AdminUser');
        $where = array('username' => $username);
        $fields = array('id', 'password', 'username', 'status', 'expire', 'logintime'); // 之查找需要的字段
        $result = $user->where($where)->field($fields)->find();

        if (!$result || $password != $result['password']) return $this->error('賬號(hào)或密碼錯(cuò)誤',U('Home/Login/index'));

        if ($result['status'] == 0) return $this->error('該用戶(hù)被鎖定,暫時(shí)不可登錄',U('Home/Login/index'));

        // 是否記住我的登錄,設(shè)置一個(gè)Cookie,寫(xiě)在客戶(hù)端
        if (isset($_POST['remember'])) {
            $value = $result['id'] . '|' . get_client_ip() . '|' . $result['username'];
            $value = encrytion($value, 1);
            @setcookie('remember', $value, C('AUTO_LOGIN_LIFETIME'), '/');
        }

        // 每天登錄增加經(jīng)驗(yàn)值
        $today = strtotime(date('Y-m-d')); // 獲取今天0時(shí)0分0秒的時(shí)間
        // 如果上次的登錄時(shí)間小于今天的時(shí)間,則增加經(jīng)驗(yàn)值
        $where2 = array('id' => $result['id']);
        if ($result['logintime'] < $today) {
            $user->where($where2)->setInc('expire', 10);
        }

        //更新登錄戶(hù)登錄信息
        $data_arr = array(
            'id' => $result['id'],
            'logintime' => time(),
            'loginip' => get_client_ip(),
        );
        if ($user->save($data_arr)) {
            // 獲取$_SESSION['user_id'] 如果不存在則默認(rèn)為0
            session('uid', 0);
            session('username', $result['username']);
            session('loginAccount', $result['username']);
            session('loginUserName', $result['username']);
            session('uid', $result['id']);
            //RBAC 開(kāi)始,用戶(hù)認(rèn)證SESSION標(biāo)記 ,默認(rèn)為"authId"
            session(C('USER_AUTH_KEY'), $result['id']);

            //如果為超級(jí)管理員,則無(wú)需驗(yàn)證
            if ($_SESSION['username'] == C('RBAC_SUPERADMIN')) session(C('ADMIN_AUTH_KEY'), true);

            //用于檢測(cè)用戶(hù)權(quán)限的方法,并保存到Session中,讀取用戶(hù)權(quán)限
            Rbac::saveAccessList($result['id']);
            //添加操作日志中
            $desc = '登陸成功';
            addOperationLog($desc);
            return $this->redirect('Index/index');
        } else {
            return $this->error('2222222222222');
        }

    }

    public function memberInfo()
    {
        $user_id = session('user_id');
        $user = D('User');
        $where = array('user_id' => $user_id);
        $result = $user->where($where)->select();
        $this->result = $result;
        $this->display();
    }

    /**
     * 獲取apikey_values
     */

    public function apikey()
    {
        $secret = "6JNVkTk4jHsgF0e1oOVLwOZDeq83pDXa";
        $user_id = I('user_id', '', int);
        // $where = array('user_id = %d ',array($user_id));
        $user = M('User');
        // $result = $user->where($where)->find();
        $result = $user->where("user_id = %d", array($user_id))->find();

        $hash = sha1($result['user_id'] . $result['password'] . $secret);
        $data = array(
            'apikey_value' => $hash,
            'apikey_time' => time()
        );
        $where = array('user_id' => $user_id);
        $user->where($where)->save($data);
        $this->ajaxReturn($hash);
    }

    public function hash()
    {
        if (!IS_POST) {
            $out_data = array(
                'err_msg' => 'request method is error.',
                'is_success' => 'Fail'
            );
            exit(json_encode($out_data));
        };

        $username = I('username');
        $password = I('password');
        $where = array('username' => $username);

        $user = M('User');
        $result = $user->where($where)->find();
        if (!$result || $password != $result['password']) {
            $out_data = array(
                'err_msg' => 'Username or password is incorrect.',
                'is_success' => 'Fail'
            );
            exit(json_encode($out_data));
        }

        /**
         * HASH生成規(guī)則
         */
        $secret = "6JNVkTk4jHsgF0e1oOVLwOZDeq83pDXa";
        $hash = sha1($result['user_id'] . $result['password'] . $secret);

        $where = array('user_id' => $result['user_id']);
        $data["apikey_value"] = $hash;
        $data["apikey_time"] = time();
        $user->where($where)->save($data);

        $out_data = array(
            'is_success' => 'Success',
            'hash' => $hash
        );
        exit(json_encode($out_data));
    }

    public function relationTest()
    {
        echo get_client_ip();
    }

    public function error1(){
        $this->display();
    }

    public function logout()
    {
        session('username', NULL);
        session_unset();
        session_destroy();
        return $this->redirect('Login/index');
    }
}

接著在控制器目錄創(chuàng)建一個(gè)CommonAction.class.php文件,然后改寫(xiě)所有要權(quán)限驗(yàn)證的類(lèi),讓其繼承自CommonAction。代碼如下:

<?php
namespace Home\Controller;

use Think\Controller;
use Org\Util\Rbac;
class BaseController extends Controller
{
//    /**
//     * ThikPHP自動(dòng)運(yùn)行方法,每一次,都會(huì)自動(dòng)運(yùn)行這個(gè)方法
//     * 要判斷一個(gè)session值是否已經(jīng)設(shè)置,可以使用
//       session('?name'); 相當(dāng)于:isset($_SESSION['name']);
//     */
    public function _initialize(){
        /***************************************網(wǎng)站開(kāi)關(guān)****************************************************/
        if(!C('WEB_STATE')) exit('網(wǎng)站維護(hù)中');

        /***********************************沒(méi)有登錄時(shí)候時(shí)需要執(zhí)行的代碼**************************************/
        if(!isset($_SESSION[C('USER_AUTH_KEY')])) return $this->redirect('Login/index');

        /***************************************自動(dòng)登錄配置************************************************/
        if(isset($_COOKIE['remember']) && !isset($_SESSION['uid'])){
            // Cookie 解密
            $value = encrytion($_COOKIE['remember']);
            $result = explode('|',$value);
            // 判斷IP地址是否一樣
            if($result[1] == get_client_ip()){
                session('uid',$result[0]);
                session('uid',$result[2]);
            }
        }

        /***************************************權(quán)限認(rèn)證****************************************************/
        $Public = in_array(MODULE_NAME,explode(',',C('NOT_AUTH_MODULE'))) || in_array(ACTION_NAME,explode(',',C('NOT_AUTH_ACTION')));
        // 如果不在公共模塊之中,同時(shí)開(kāi)啟權(quán)限驗(yàn)證的話,則開(kāi)始認(rèn)證過(guò)程
        if(C('USER_AUTH_ON') && !$Public)
        {
            if(!Rbac::AccessDecision()) //通過(guò)accessDecision獲取權(quán)限信息,true:表示有操作權(quán)限,false:無(wú)操作權(quán)限
            {
                return $this->error("你沒(méi)有對(duì)應(yīng)的權(quán)限");  //沒(méi)有獲取到權(quán)限信息時(shí)需要執(zhí)行的代碼
            }
        }
        /***************************************導(dǎo)航欄菜單顯示****************************************************/
        /*
        *   思路:
        *   1.取出所有權(quán)限節(jié)點(diǎn)。
        *   2.取出當(dāng)前登錄用戶(hù)擁有的模塊權(quán)限(取英文名稱(chēng))和操作權(quán)限(取ID)
        *   3.對(duì)所有權(quán)限進(jìn)行遍歷,先匹配模塊權(quán)限,不存在刪除,存在則匹配操作權(quán)限
        */
        // 超級(jí)管理員登錄
        if(session(C('ADMIN_AUTH_KEY')))
        {
            $menus = D('AdminNode')->where('level = 2')->relation(true)->order('sort desc')->select();//取出所有節(jié)點(diǎn)
        }else{
            /**
             * [1]取出所有的權(quán)限,是通過(guò)關(guān)聯(lián)模型從數(shù)據(jù)庫(kù)中獲取的
             */
            $menus = D('AdminNode')->where('level = 2')->relation(true)->order('sort desc')->select();
            $module = '';    //存放擁有的模塊
            $node_id = '';   //存放擁有的模塊
            /**
             * [2]獲取當(dāng)前用戶(hù)的所有權(quán)限,這個(gè)是從RBAC中獲取的
             */
            $access_list = $_SESSION['_ACCESS_LIST'];   //當(dāng)前用戶(hù)所擁有的權(quán)限
            foreach ($access_list as $key => $value) {
                foreach ($value as $key1 => $value1) {
                    $module = $module.','.$key1;    //字符串拼接模塊名稱(chēng)
                    foreach ($value1 as $key2 => $value2) {
                        $node_id = $node_id.','.$value2;    //字符串拼操作id
                    }
                }
            }
            /**
             * [3]去除沒(méi)有權(quán)限的節(jié)點(diǎn),通過(guò)所有權(quán)限和用戶(hù)已經(jīng)擁有的權(quán)限比較
             */
            foreach ($menus as $key => $value) {
                $all_node[] = $value['name'];
                if(!in_array(strtoupper($value['name']), explode(',', $module))){
                    unset($menus[$key]);    //刪除模塊
                }else{
                    //模塊存在,比較里面的操作
                    foreach ($value['node'] as $key1 => $value1) {
                        if(!in_array($value1['id'], explode(',', $node_id))){
                            unset($menus[$key]['node'][$key1]);  // 刪除操作
                        }
                    }
                }
            }
        }
        $this->menus = $menus;
    }

}

ThinkPHP中提供了一個(gè)_initialize()方法,是在類(lèi)初始化就會(huì)執(zhí)行的,也就是只要后面控制器繼承自CommonAction類(lèi),就會(huì)在作對(duì)應(yīng)操作時(shí),執(zhí)行_initialize()方法。

以上是ThinkPHP中RBAC是什么的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI