ThinkPHP提供了基于RBAC和基于AUTH的兩種權限。
基于角色的訪問控制(Role-Based Access Control)作為傳統訪問控制(自主訪問,強制訪問)的有前景的代替受到廣泛的關注。在RBAC中,權限與角色相關聯,用戶通過成為適當角色的成員而得到這些角色的權限。這就極大地簡化了權限的管理。在一個組織中,角色是為了完成各種工作而創造,用戶則依據它的責任和資格來被指派相應的角色,用戶可以很容易地從一個角色被指派到另一個角色。角色可依新的需求和系統的合并而賦予新的權限,而權限也可根據需要而從某角色中回收。角色與角色的關系可以建立起來以囊括更廣泛的客觀情況。
### 源碼解析
~~~
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2011 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: luofei614 <weibo.com/luofei614>
// +----------------------------------------------------------------------
namespace Think;
/**
* 權限認證類
* 功能特性:
* 1,是對規則進行認證,不是對節點進行認證。用戶可以把節點當作規則名稱實現對節點進行認證。
* $auth=new Auth(); $auth->check('規則名稱','用戶id')
* 2,可以同時對多條規則進行認證,并設置多條規則的關系(or或者and)
* $auth=new Auth(); $auth->check('規則1,規則2','用戶id','and')
* 第三個參數為and時表示,用戶需要同時具有規則1和規則2的權限。 當第三個參數為or時,表示用戶值需要具備其中一個條件即可。默認為or
* 3,一個用戶可以屬于多個用戶組(think_auth_group_access表 定義了用戶所屬用戶組)。我們需要設置每個用戶組擁有哪些規則(think_auth_group 定義了用戶組權限)
*
* 4,支持規則表達式。
* 在think_auth_rule 表中定義一條規則時,如果type為1, condition字段就可以定義規則表達式。 如定義{score}>5 and {score}<100 表示用戶的分數在5-100之間時這條規則才會通過。
*/
//數據庫
/*
-- ----------------------------
-- think_auth_rule,規則表,
-- id:主鍵,name:規則唯一標識, title:規則中文名稱 status 狀態:為1正常,為0禁用,condition:規則表達式,為空表示存在就驗證,不為空表示按照條件驗證
-- ----------------------------
DROP TABLE IF EXISTS `think_auth_rule`;
CREATE TABLE `think_auth_rule` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`name` char(80) NOT NULL DEFAULT '',
`title` char(20) NOT NULL DEFAULT '',
`type` tinyint(1) NOT NULL DEFAULT '1',
`status` tinyint(1) NOT NULL DEFAULT '1',
`condition` char(100) NOT NULL DEFAULT '', # 規則附件條件,滿足附加條件的規則,才認為是有效的規則
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- ----------------------------
-- think_auth_group 用戶組表,
-- id:主鍵, title:用戶組中文名稱, rules:用戶組擁有的規則id, 多個規則","隔開,status 狀態:為1正常,為0禁用
-- ----------------------------
DROP TABLE IF EXISTS `think_auth_group`;
CREATE TABLE `think_auth_group` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`title` char(100) NOT NULL DEFAULT '',
`status` tinyint(1) NOT NULL DEFAULT '1',
`rules` char(80) NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- ----------------------------
-- think_auth_group_access 用戶組明細表
-- uid:用戶id,group_id:用戶組id
-- ----------------------------
DROP TABLE IF EXISTS `think_auth_group_access`;
CREATE TABLE `think_auth_group_access` (
`uid` mediumint(8) unsigned NOT NULL,
`group_id` mediumint(8) unsigned NOT NULL,
UNIQUE KEY `uid_group_id` (`uid`,`group_id`),
KEY `uid` (`uid`),
KEY `group_id` (`group_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
*/
class Auth{
//默認配置
protected $_config = array(
'AUTH_ON' => true, // 認證開關
'AUTH_TYPE' => 1, // 認證方式,1為實時認證;2為登錄認證。
'AUTH_GROUP' => 'auth_group', // 用戶組數據表名
'AUTH_GROUP_ACCESS' => 'auth_group_access', // 用戶-用戶組關系表
'AUTH_RULE' => 'auth_rule', // 權限規則表
'AUTH_USER' => 'member' // 用戶信息表
);
public function __construct() {
$prefix = C('DB_PREFIX');
$this->_config['AUTH_GROUP'] = $prefix.$this->_config['AUTH_GROUP'];
$this->_config['AUTH_RULE'] = $prefix.$this->_config['AUTH_RULE'];
$this->_config['AUTH_USER'] = $prefix.$this->_config['AUTH_USER'];
$this->_config['AUTH_GROUP_ACCESS'] = $prefix.$this->_config['AUTH_GROUP_ACCESS'];
if (C('AUTH_CONFIG')) {
//可設置配置項 AUTH_CONFIG, 此配置項為數組。
$this->_config = array_merge($this->_config, C('AUTH_CONFIG'));
}
}
/**
* 檢查權限
* @param name string|array 需要驗證的規則列表,支持逗號分隔的權限規則或索引數組
* @param uid int 認證用戶的id
* @param string mode 執行check的模式
* @param relation string 如果為 'or' 表示滿足任一條規則即通過驗證;如果為 'and'則表示需滿足所有規則才能通過驗證
* @return boolean 通過驗證返回true;失敗返回false
*/
public function check($name, $uid, $type=1, $mode='url', $relation='or') {
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 = array($name);
}
}
$list = array(); //保存驗證通過的規則名
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 ) { //如果節點相符且url參數滿足
$list[] = $auth ;
}
}else if (in_array($auth , $name)){
$list[] = $auth ;
}
}
if ($relation == 'or' and !empty($list)) {
return true;
}
$diff = array_diff($name, $list);
if ($relation == 'and' and empty($diff)) {
return true;
}
return false;
}
/**
* 根據用戶id獲取用戶組,返回值為數組
* @param uid int 用戶id
* @return array 用戶所屬的用戶組 array(
* array('uid'=>'用戶id','group_id'=>'用戶組id','title'=>'用戶組名稱','rules'=>'用戶組擁有的規則id,多個,號隔開'),
* ...)
*/
public function getGroups($uid) {
static $groups = array();
if (isset($groups[$uid]))
return $groups[$uid];
$user_groups = M()
->table($this->_config['AUTH_GROUP_ACCESS'] . ' a')
->where("a.uid='$uid' and g.status='1'")
->join($this->_config['AUTH_GROUP']." g on a.group_id=g.id")
->field('uid,group_id,title,rules')->select();
$groups[$uid]=$user_groups?:array();
return $groups[$uid];
}
/**
* 獲得權限列表
* @param integer $uid 用戶id
* @param integer $type
*/
protected function getAuthList($uid,$type) {
static $_authList = array(); //保存用戶驗證通過的權限列表
$t = implode(',',(array)$type);
if (isset($_authList[$uid.$t])) {
return $_authList[$uid.$t];
}
if( $this->_config['AUTH_TYPE']==2 && isset($_SESSION['_AUTH_LIST_'.$uid.$t])){
return $_SESSION['_AUTH_LIST_'.$uid.$t];
}
//讀取用戶所屬用戶組
$groups = $this->getGroups($uid);
$ids = array();//保存用戶所屬用戶組設置的所有權限規則id
foreach ($groups as $g) {
$ids = array_merge($ids, explode(',', trim($g['rules'], ',')));
}
$ids = array_unique($ids);
if (empty($ids)) {
$_authList[$uid.$t] = array();
return array();
}
$map=array(
'id'=>array('in',$ids),
'type'=>$type,
'status'=>1,
);
//讀取用戶組所有權限規則
$rules = M()->table($this->_config['AUTH_RULE'])->where($map)->field('condition,name')->select();
//循環規則,判斷結果。
$authList = array(); //
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
$_SESSION['_AUTH_LIST_'.$uid.$t]=$authList;
}
return array_unique($authList);
}
/**
* 獲得用戶資料,根據自己的情況讀取數據庫
*/
protected function getUserInfo($uid) {
static $userinfo=array();
if(!isset($userinfo[$uid])){
$userinfo[$uid]=M()->where(array('uid'=>$uid))->table($this->_config['AUTH_USER'])->find();
}
return $userinfo[$uid];
}
}
~~~
### 原理
1. 判斷當前的操作(項目【應用】,模塊、動作(操作)是否需要認證)對應節點(Node表里和配置里和AUTH相關的配置如NOT_AUTH_MODULE、REQUIRE_AUTH_MODULE、NOT_AUTH_ACTION、REQUIRE_AUTH_ACTION也就是需要認證的模塊、不需要認證的模塊、需要認證的操作和不需要認證的操作啦)。
2. 如果需要認證(判斷用戶是否登錄,如果沒登錄-->跳至委托認證管理器驗證身份,判斷用戶是否有權限訪問如果沒權限--直接跳至無權訪問頁面)
3. 委托認證來驗證用戶身份
4. 獲取該用戶的權限列表
5. 判斷用戶是否有權限訪問
# [案例解釋說明](案例解釋說明.md)
- 序言
- 基礎知識
- thinkphp基礎知識
- Thinkphp5CURD
- 數據庫創建
- 數據庫刪除
- 數據庫更新
- 數據庫查詢
- thinkphp5控制器
- 空操作空控制器
- 控制器基類
- 請求信息
- 行為和鉤子
- thinkphp5路由設置
- 變量路由
- 常用方法清單
- 環境搭建
- lnmp
- 升級php
- window環境
- Thinkphp小案例
- 分類管理
- 數據庫設計
- 模型
- 控制器
- 視圖
- 文件上傳
- 上傳接口
- 視圖
- 表單提交
- 視圖設計
- 控制器
- 權限控制
- 案例解釋說明
- 登錄驗證
- Laravel5.3登錄模式
- redis使用
- 一鍵安裝
- 程序設計與實現
- 頁面設計
- 功能設計
- 安裝功能實際
- 函數庫
- 配置文件
- 清除緩存
- 狀態值修改
- 數據庫備份還原
- controller.php
- common.php
- index.html
- importlist.html
- 完整的增刪查改
- 查詢語句
- 多語言支持
- JpGraph圖表類庫
- 微信支付
- payBase.php
- Order.php
- Oauth.php
- Jspay.php
- 下載遠程地址中的圖片
- URL重寫隱藏入口文件
- 圖片水印
- 整合百度編輯器
- Ueditor
- ueditor完整配置項
- 配置信息常見的方式
- HTTP 斷點續傳(PHP實現)
- layui.upload上傳文件或圖片
- QQ微信域名防封 預防域名封禁 強制跳轉至瀏覽器
- 蜘蛛篇
- 超簡單實現php谷歌驗證
- 采集金山詞霸每日一句
- think-swoole
- 原生PHP小案例
- 查詢修改數據庫
- mysql支付回調源碼
- pdo連接微信退款
- 前端小案例
- html快捷查詢
- layui經驗總結
- layui 表單增強插件
- Vue列表Ajax實戰教程
- PHP基礎
- 類的自動載入
- php基礎函數- 字符串函數
- php基礎函數-數學函數
- php基礎函數-數組函數
- PHP常見排序算法學習
- 請求第三方
- 從網絡下載文件
- 檢查網站是否宕機
- file_get_contents
- 算法
- php 抽獎算法(適合九宮格和大轉盤)
- 自己動手豐衣足食
- 入口文件
- start.php
- app.php
- load.php
- route.php
- JqHttp
- Jqfile
- Jqutil
- pdo連接數據庫類
- 常見的php類
- php數據接口類
- 生成多層樹狀下拉選框的工具模型
- 上傳下載類
- 微信用戶相關類
- Zip壓縮類
- 列表樹生成工具類
- 日期時間操作類
- 文件及文件夾處理類
- 字符串處理類
- php守護進程類
- RSA算法類
- php支持中英文的加密解密類
- CURL多線程請求
- 通用數據庫操作類
- 緩存類
- cookie類
- 常見的驗證方法
- 隨機密鑰
- 日志Log
- php-redis 操作類 封裝
- OpensslRsa 加密、解密、簽名、驗簽類
- 模板輸出類
- 發送郵件
- 封裝的mysqli類
- PHP時間段分割類庫
- PHP apk解包識版本號信息和ipa包信息
- 訪問客戶端信息
- http請求
- PHP 無數據庫讀寫配置文件
- 自己動手寫一個jwt類
- php實現對圖片對稱加解密(適用身份證加密等場景)
- 常見php函數
- 無限分類
- 獲取文章圖片
- 加密解密
- JSON數據輸出(適合在tp中)
- 刪除目錄和文件
- 判斷是否為手機訪問
- 獲取客戶端真實IP
- 隨機生成ip地址
- 字符串與二進制進行轉換
- 對數組進行排序
- 格式化字節大小
- 時間戳格式化
- 獲取數據的所有子孫數據的id值
- 取得視頻文件的縮略圖
- 圖片裁剪函數
- 按照每過0:00算一天
- 下載文件
- PHP隨機密碼生成
- 判斷數字大小
- 報文組成
- 通過ip定位城市
- PDO方式連接MySQL數據庫
- 數組與xml
- php字符串處理函數
- 判斷是否ajax提交
- 生成概率,用于抽獎
- 斷點續傳
- PHP使用星號替代用戶名手機和郵箱
- 獲取毫秒級別的時間戳
- php日志函數
- 隨機顏色生成器
- 時間差異計算函數
- 黑名單過濾
- 常見PHP 正則表達式
- php獲取瀏覽器類型
- 郵件發送
- 獲取qq昵稱
- 正則獲取手機號歸屬地
- 判斷是否是移動客戶端 移動設備
- gbk和utf8編碼自動識別方法
- 人性化時間顯示
- 請求API接口
- 數據庫備份
- PHP并發下安全讀寫文件函數
- PHP讀取exe軟件版本號
- PHP為任意頁面設置訪問密碼
- PHP利用百度當圖床
- 秒/分鐘/小時前
- 常見的js函數
- 短信驗證函數
- 上下收縮菜單
- jQuery 樹插件zTree
- 頁面刷新跳轉
- jquery導出報表
- js實現定時效果
- 獲取當前經緯度
- JQuery實現圖片大小自適應
- 網站運行時間
- 判斷瀏覽器類型
- 百度推送
- js對指定數據進行排序
- 常見工具方法
- JSPinyin
- 技術相關文章
- 高級PHP工程師所應該具備哪些技能
- 最簡潔的PHP程序員學習路線及建議
- 優化PHP代碼的一些建議
- TP5性能優化建議
- 程序猿專用代碼注釋:佛祖保佑,永無BUG
- 一組匹配中國大陸手機號碼的正則表達式
- Apache/Nginx/PHP服務器反爬蟲代碼大全
- 番外
- 配置shadowsocks服務端
- python
- go
- 如何在1分鐘內黑掉任何網站!
- 百度貼吧敏感詞
- 貼吧手工養號發帖教程
- 搞笑的注釋代碼
- Heroku