分類目錄歸檔:易點(diǎn)開(kāi)源

易點(diǎn)開(kāi)源

ACL – 權(quán)限控制

權(quán)限控制

權(quán)限控制顧名思義就是控制什么“人”能(不能)訪問(wèn)什么(“操作”)。在身份認(rèn)證中,我們已經(jīng)知道了人是誰(shuí),現(xiàn)在這里就介紹yangzie如何控制這些人能做什么,yangzie是基于ACL來(lái)實(shí)現(xiàn)授權(quán)功能的,那么這里需要先闡明幾個(gè)概念:

Access Control Object

ACO訪問(wèn)控制對(duì)象,也就是ARO要請(qǐng)求訪問(wèn)的對(duì)象,在yangzie中也就是M/C/A(見(jiàn)yangzie基本原則),每個(gè)具體的ACO也有一個(gè)標(biāo)識(shí),就按照M/C/A的格式進(jìn)行標(biāo)識(shí),比如order模塊下面的增加訂單add,假如他都組織在index控制器下(index_controller),action為add;那么該ACO的標(biāo)識(shí)就是:/order/index/add;

根據(jù)具體的業(yè)務(wù)要求,ACO可以定義到某個(gè)具體的Action級(jí)別,或者Controller級(jí)別或者整個(gè)module級(jí)別

Access Request Object

ARO訪問(wèn)請(qǐng)求對(duì)象,也就是要請(qǐng)求ACO的對(duì)象,通常他指“人”,各種各樣的類型的人,每種“類型”有一個(gè)唯一的標(biāo)識(shí),用于區(qū)別一類人,比如某種角色的,也可以唯一標(biāo)識(shí)一個(gè)人;

這個(gè)標(biāo)識(shí)yangzie的設(shè)計(jì)是從大類/小類/具體某個(gè)用戶id然后以/進(jìn)行分割,舉個(gè)例子,假如系統(tǒng)中存在這這幾種角色,超級(jí)管理員,普通管理員,銷售;可能這幾種角色的ARO標(biāo)識(shí)看起來(lái)是這個(gè)樣子:

  • 超級(jí)管理員:/admin/root
  • 普通管理員:/admin/normal
  • 銷售:/salesman
  • 管理員A:/admin/normal/12344

為什么要這樣設(shè)計(jì),是因?yàn)樵谶M(jìn)行授權(quán)時(shí)可以把權(quán)限分配給組或者具體某個(gè)類型或者具體某個(gè)人;然后具體的某個(gè)用戶,如上面的管理員A,在驗(yàn)證權(quán)限時(shí),再?gòu)木唧wid到大類來(lái)逐一驗(yàn)證,直到找到具體的權(quán)限是允許還是拒絕為止。

ACL

ARO和ACO分別定義了“操作”和“人”,那么剩下的就是把這兩個(gè)關(guān)聯(lián)起來(lái),從而達(dá)到什么人能訪問(wèn)什么的目的;這個(gè)關(guān)聯(lián)的地方就是AccessControlList;在yangzie中它是一個(gè)配置文件,位于app/__aros_acos__.php;默認(rèn)的內(nèi)容如下:


function yze_get_aco_desc($aconame) {
    foreach ( ( array ) yze_get_acos_aros () as $aco => $desc ) {
        if (preg_match ( "{^" . $aco . "}", $aconame )) {
            return @$desc ['desc'];
        }
    }
    return '';
}
function yze_get_ignore_acos() {
    return array();
}
function yze_get_acos_aros() {
    $array = array (
            "/" => array (//module/controller/action
                    "deny" => "",
                    "allow" => array (
                            "*" //aro
                    ),
                    "desc" => ""http://功能說(shuō)明 
            )
		);
    
	return $array;
}

該文件主要包含3個(gè)函數(shù):

  • yze_get_acos_aros:該方法返回ACL列表
  • yze_get_ignore_acos:該方法返回忽略權(quán)限控制的ACO,當(dāng)ARO請(qǐng)求這些內(nèi)容時(shí)忽略權(quán)限控制
  • yze_get_aco_desc: 助手方法,返回ACO功能的描述,

ACL的格式

ACL是一個(gè)數(shù)組,格式如下:

array (
            "ACO1 name" => array (
                    "deny" => "",//拒絕的ARO
                    "allow" => array (
                            "*" //允許的ARO
                    ),
                    "desc" => ""http://ACO功能說(shuō)明 
            ),
            "ACO2 name" => array (
                    "deny" => "",//拒絕的ARO
                    "allow" => "*", //允許的ARO,
                    "desc" => ""http://ACO功能說(shuō)明 
            ),
);

ACO name是ACO的標(biāo)識(shí),根據(jù)權(quán)限控制的級(jí)別可以具體定義到Action級(jí)別,或者只定義到Module級(jí)別;

deny定義黑名單,拒絕里面列出的ARO的訪問(wèn);allow定義白名單,允許里面列出的ARO

ACL定義規(guī)則

  1. ACO和ARO都可以使用正則表達(dá)
  2. 黑名單優(yōu)先級(jí)大于白名單
  3. 兩者都可以采用”*”來(lái)代表所有
  4. 如果要指定具體的aro,deny和allow中以數(shù)組的形式列出ARO

一個(gè)真實(shí)的例子:


function yze_get_ignore_acos() {
    return array(
            "/card/front",
            "/package/package/grab",
            "/package/package/grabfail",
            "/package/front",
            "/zb/middlepage/view",
            "/wxbbs/front",
            "/zbad/front",
    );
}
function yze_get_acos_aros() {
    $array = array (
            "/admin" => array (
                    "deny" => "*",
                    "allow" => array (
                            "/admin" 
                    ),
                    "desc" => "后端管理" 
            ),
            "/sp/consumers" => array (
                    "deny" => "*",
                    "allow" => array (
                            "/sp"
                    ),
                    "desc" => "粉絲管理"
            )
    );
    if (YDWXP_TYPE == YDWXP_TYPE_MARKET || YDWXP_TYPE == YDWXP_TYPE_ALL) {
        $array ['/card'] = array (
                "deny" => "*",
                "allow" => array ("/sp"),
                "desc" => "卡券管理" 
        );
        $array ['/card/card/*consume'] = array (
                "deny" => "*",
                "allow" => array ("/sp"),
                "desc" => "卡券核銷"
        );
        $array ['/card/merchants'] = array (
                "deny" => "*",
                "allow" => array ("/sp/super","/sp/common"),
                "desc" => "商戶管理" 
        );
    }
    
	return $array;
}

ARO怎么定義

ACO是根據(jù)M/C/A來(lái)定義的,這很明確和具體,但ARO標(biāo)識(shí)怎么根據(jù)用戶來(lái)定義?這也是通過(guò)hook來(lái)實(shí)現(xiàn)的,在app/hooks/auth.php中已經(jīng)定義注冊(cè)了該hook,開(kāi)發(fā)者只需要實(shí)現(xiàn)即可,這里有一個(gè)真實(shí)的例子:


YZE_Hook::add_hook ( YZE_FILTER_GET_USER_ARO_NAME, function  ( $data ) {
	if ( !@$_SESSION [ 'admin' ] )return "/";
	if(is_a($_SESSION['admin'], "\\app\\sp\\Consumer_Model")){
	   return "/consumer";
	}
    if($_SESSION [ 'admin' ]->sp_id) {
       //是否子商戶
       if ( ! isset($_SESSION [ 'is_sub_merchant' ]) ) {
           $_SESSION [ 'is_sub_merchant' ] = YZE_Hook::do_hook(YDMARKET_IS_SUB_MERCHANT, $_SESSION [ 'admin' ]->sp_id);
       }
       if ($_SESSION [ 'is_sub_merchant' ]) {
           return "/".$_SESSION [ 'admin' ]->user->type."/sub/".$_SESSION [ 'admin' ]->type;
       }
    }
	return "/".$_SESSION [ 'admin' ]->user->type."/".$_SESSION [ 'admin' ]->type;
} );

根據(jù)自己的業(yè)務(wù)系統(tǒng)的用戶類型,自行設(shè)計(jì)自己的ARO標(biāo)識(shí)接口。

能靈活的配置權(quán)限嗎?

ACL列表屬于事先硬編碼的控制列表,但在實(shí)際的業(yè)務(wù)環(huán)節(jié)中很多權(quán)限都是需要?jiǎng)討B(tài)分配的,比如給某中角色設(shè)置權(quán)限,給具體某個(gè)人設(shè)置權(quán)限;這在yangzie中如何做呢?

yangzie有兩個(gè)預(yù)留函數(shù)get_permissions($aroname),get_user_permissions()

這兩個(gè)函數(shù)由開(kāi)發(fā)者實(shí)現(xiàn),分別返回指定aroname的acl或者返回某個(gè)具體用戶的acl,這通常是具體的業(yè)務(wù)要求決定的,返回的仍然是acl數(shù)組結(jié)構(gòu)。這兩個(gè)函數(shù)放在任意的自動(dòng)包含文件中(見(jiàn)__config__.php說(shuō)明),但要注意

這兩個(gè)函數(shù)必須屬于頂級(jí)命名空間

當(dāng)有這兩個(gè)函數(shù)的實(shí)現(xiàn)時(shí),他們的返回結(jié)果將覆蓋ACL里面的控制

ACL的效果是什么

當(dāng)配置好ACL后,如果一個(gè)用戶訪問(wèn)被deny的內(nèi)容,如某個(gè)action,某個(gè)controller或者某個(gè)module;yangzie將會(huì)拋出YZE_Permission_Deny_Exception異常,開(kāi)發(fā)者可以通過(guò)hook YZE_FILTER_YZE_EXCEPTION來(lái)監(jiān)聽(tīng)然后做響應(yīng)的處理,默認(rèn)情況下將顯示500頁(yè)面;

這就是說(shuō)yangzie會(huì)根據(jù)ACL來(lái)驗(yàn)證用戶的權(quán)限,如果用戶沒(méi)有權(quán)限就會(huì)立即拋出異常,這在進(jìn)入開(kāi)發(fā)者的業(yè)務(wù)邏輯之前就已經(jīng)處理,開(kāi)發(fā)者無(wú)需在自己的功能代碼中去驗(yàn)證當(dāng)前用戶是誰(shuí),是否有權(quán)限方法;開(kāi)發(fā)者就只關(guān)注自己的業(yè)務(wù)邏輯即可。

GET和POST的特殊情況

yangzie的Action包含兩種情況,get方式和post方法,比如一個(gè)增加用戶的例子,假如M/C/A是:users/index/add;那么他對(duì)應(yīng)的post action就是users/index/post_add,會(huì)多一個(gè)post前綴,如果在ACL需要針對(duì)這兩種情況單獨(dú)控制,比如允許get但不允許post,也就是允許看,但不允許提交,那在ACL中需要分配針對(duì)兩個(gè)action(add,post_add)進(jìn)行處理

如何通過(guò)ACL來(lái)控制輸出

在實(shí)際的業(yè)務(wù)系統(tǒng)中,通常需要根據(jù)權(quán)限來(lái)控制輸出的響應(yīng)中某些部分用戶可見(jiàn),某些部分用戶不可見(jiàn),那么這可以通過(guò)兩種方式來(lái)實(shí)現(xiàn)。

方法一:通過(guò)YZE_ACL::get_instance()->check_byname($aroname, $aconame)來(lái)判斷并輸出


if(YZE_ACL::get_instance()->check_byname($aroname, $aconame)){

// your output html

}

方法二:通過(guò)YZE_ACL::get_instance()->begin_check_permission($id, $aroname, $aconame)….YZE_ACL::get_instance()->end_check_permission($id, $aroname, $aconame)來(lái)輸出


YZE_ACL::get_instance()->begin_check_permission($id, $aroname, $aconame)

// your output html

YZE_ACL::get_instance()->end_check_permission($id, $aroname, $aconame)

根據(jù)你的習(xí)慣來(lái)選擇,如果你不喜歡跨度很大的if(){},那用第二種方式是個(gè)好選擇

yangzie代碼結(jié)構(gòu)

yangzie的目錄結(jié)構(gòu)

yangzie 目錄是框架核心文件

scripts是構(gòu)建腳本目錄

tests是單元測(cè)試文件目錄

tmp是其他一些臨時(shí)目錄

app是功能代碼目錄,我們編寫(xiě)的功能代碼都在其中

APP目錄詳細(xì)說(shuō)明

  • __aros_acos__.php 該文件是ACL控制配置文件,這將在ACL控制中詳細(xì)說(shuō)明
  • __config__.php是系統(tǒng)的配置文件,包含如數(shù)據(jù)庫(kù)配置資源打包綁定,文件包含登錄
  • hooks是系統(tǒng)級(jí)別的hook注冊(cè)文件放置目錄
  • modules是功能模塊目錄,所有的業(yè)務(wù)功能代碼都會(huì)以modules的方式放置在這里面
    • controllers是所有控制器類文件
    • models是所有的model文件,model是與數(shù)據(jù)庫(kù)的表對(duì)應(yīng)的類,這將在Model-數(shù)據(jù)處理中說(shuō)明
    • views是控制器的方法對(duì)應(yīng)的輸出視圖,這將在視圖系統(tǒng)中進(jìn)行介紹
    • hooks是該模塊下的hooks文件
    • __module__.php是模塊的配置文件
  • public_html是系統(tǒng)訪問(wèn)的入口目錄,里面的目錄可以自由組織存放
  • public_html/index.php就是入口文件
  • public_html/module-assets是modules對(duì)應(yīng)的資源文件
  • vendor是其他第三方庫(kù),layout,views等系統(tǒng)公共部分部分的放置路徑
  • vendor/layout存放的是系統(tǒng)的布局文件
  • vendor/views/存放的是公共視圖

目錄大概就了解這些,具體目錄里面的含義我們會(huì)在后面繼續(xù)詳解。

接下來(lái),開(kāi)始寫(xiě)代碼吧:《hello yangzie

配置Rewrite

  • 修改本地的hosts文件,加上127.0.0.1 yourdomain.com
    Windows Mac & Linux
    C:\WINDOWS\system32\drivers\etc\hosts /etc/hosts
  • 配置rewrite
    Apache Ngnix
    0. 啟用rewrite module:
    LoadModule rewrite_module libexec/apache2/mod_rewrite.so
    1. 修改apache配置文件httpd.conf, 取消下面這行代碼的注釋
    Include /private/etc/apache2/extra/httpd-vhosts.conf
    去掉前面的#
    2. 修改httpd-vhosts.conf<VirtualHost *:80>
    ServerAdmin webmaster@dummy-host.example.com
    DocumentRoot “/path-to-your-yangzie-dir/app/public_html”
    ServerName YOUR-DOMAIN.com
    ServerAliasYOUR-DOMAIN.com
    ErrorLog “/path-to-your-log-dir/YOUR-DOMAIN.com-error_log”
    CustomLog “/path-to-your-log-dir/YOUR-DOMAIN.com-access_log” common
    <Directory “/path-to-your-yangzie-dir/app/public_html”>
    AllowOverride All
    Require all granted
    </Directory>
    </VirtualHost># 如果localhost不能訪問(wèn),則加上下面這句
    <VirtualHost *:80>
    ServerAdmin webmaster@dummy-host2.example.com
    DocumentRoot “/path-to-your-localhost-dir”
    ServerName localhost
    ErrorLog “/path-to-your-log-dir/localhost-error_log”
    CustomLog “/path-to-your-log-dir/localhost-access_log” common
    <Directory “/path-to-your-localhost-dir”>
    Options All
    AllowOverride All
    Require all granted
    </Directory>
    </VirtualHost>
    1. 修改nginx配置文件如下:
    server {
    root path-to-your-yangzie-dir/app/public_html;
    index index.html index.php index.htm;server_name YOUR_DOMAIN.com
    }

yangzie-PHP 一個(gè)恰到好處的小巧的PHP開(kāi)發(fā)框架

yangzie是什么?

一個(gè) PHP 框架;僅此而已。

你問(wèn)它有什么優(yōu)點(diǎn)?其實(shí)它的優(yōu)點(diǎn)別的框架都有,它的缺點(diǎn)別的框架也有。

那為什么它有存在的價(jià)值?因?yàn)槲?strong>不想重復(fù):我不想在同一個(gè)項(xiàng)目中寫(xiě)重復(fù)的代碼;我不想在不同的項(xiàng)目中寫(xiě)重復(fù)的代碼;我不想年復(fù)一年的寫(xiě)重復(fù)的代碼。

yangzie是一個(gè)輕便簡(jiǎn)潔的php快速開(kāi)發(fā)框架,采用MVC開(kāi)發(fā)模式,基于module來(lái)開(kāi)發(fā)組織功能;在M、V、C和module各方面都盡力做到重用

如何使用?

  1. 下載代碼?https://github.com/ydhl/yangzie
  2. 配置本地虛擬域名
  3. 完成!打開(kāi)瀏覽器,訪問(wèn)YOUR-DOMAIN.com
  4. 如果你本地安裝的是php5.4 以上的版本,則無(wú)需配置apache,只需
    1. cd 進(jìn)入到項(xiàng)目的public_html目錄
    2. php -S localhost:8080
    3. 瀏覽器直接訪問(wèn)localhost:8080 即可

接下來(lái)

本文檔將解釋如何使用yangzie來(lái)開(kāi)發(fā),你只需根據(jù)我們的節(jié)奏一步一步走下去

了解一下《1.yangzie的目錄結(jié)構(gòu)》,知道自己寫(xiě)的代碼在哪里


為什么叫yangzie?

揚(yáng)子鱷(Alligator sinensis)或稱作鼉(tuó), 是中國(guó)特有的一種鱷魚(yú),是世界上最鱷魚(yú)