Category Archives: YDHL Opensource

YDHL Opensource

ACL – 權限控制

權限控制

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

Access Control Object

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

根據具體的業務要求,ACO可以定義到某個具體的Action級別,或者Controller級別或者整個module級別

Access Request Object

ARO訪問請求對象,也就是要請求ACO的對象,通常他指“人”,各種各樣的類型的人,每種“類型”有一個唯一的標識,用于區別一類人,比如某種角色的,也可以唯一標識一個人;

這個標識yangzie的設計是從大類/小類/具體某個用戶id然后以/進行分割,舉個例子,假如系統中存在這這幾種角色,超級管理員,普通管理員,銷售;可能這幾種角色的ARO標識看起來是這個樣子:

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

為什么要這樣設計,是因為在進行授權時可以把權限分配給組或者具體某個類型或者具體某個人;然后具體的某個用戶,如上面的管理員A,在驗證權限時,再從具體id到大類來逐一驗證,直到找到具體的權限是允許還是拒絕為止。

ACL

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


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://功能說明 
            )
		);
    
	return $array;
}

該文件主要包含3個函數:

  • yze_get_acos_aros:該方法返回ACL列表
  • yze_get_ignore_acos:該方法返回忽略權限控制的ACO,當ARO請求這些內容時忽略權限控制
  • yze_get_aco_desc: 助手方法,返回ACO功能的描述,

ACL的格式

ACL是一個數組,格式如下:

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

ACO name是ACO的標識,根據權限控制的級別可以具體定義到Action級別,或者只定義到Module級別;

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

ACL定義規則

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

一個真實的例子:


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是根據M/C/A來定義的,這很明確和具體,但ARO標識怎么根據用戶來定義?這也是通過hook來實現的,在app/hooks/auth.php中已經定義注冊了該hook,開發者只需要實現即可,這里有一個真實的例子:


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

根據自己的業務系統的用戶類型,自行設計自己的ARO標識接口。

能靈活的配置權限嗎?

ACL列表屬于事先硬編碼的控制列表,但在實際的業務環節中很多權限都是需要動態分配的,比如給某中角色設置權限,給具體某個人設置權限;這在yangzie中如何做呢?

yangzie有兩個預留函數get_permissions($aroname),get_user_permissions()

這兩個函數由開發者實現,分別返回指定aroname的acl或者返回某個具體用戶的acl,這通常是具體的業務要求決定的,返回的仍然是acl數組結構。這兩個函數放在任意的自動包含文件中(見__config__.php說明),但要注意

這兩個函數必須屬于頂級命名空間

當有這兩個函數的實現時,他們的返回結果將覆蓋ACL里面的控制

ACL的效果是什么

當配置好ACL后,如果一個用戶訪問被deny的內容,如某個action,某個controller或者某個module;yangzie將會拋出YZE_Permission_Deny_Exception異常,開發者可以通過hook YZE_FILTER_YZE_EXCEPTION來監聽然后做響應的處理,默認情況下將顯示500頁面;

這就是說yangzie會根據ACL來驗證用戶的權限,如果用戶沒有權限就會立即拋出異常,這在進入開發者的業務邏輯之前就已經處理,開發者無需在自己的功能代碼中去驗證當前用戶是誰,是否有權限方法;開發者就只關注自己的業務邏輯即可。

GET和POST的特殊情況

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

如何通過ACL來控制輸出

在實際的業務系統中,通常需要根據權限來控制輸出的響應中某些部分用戶可見,某些部分用戶不可見,那么這可以通過兩種方式來實現。

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


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

// your output html

}

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


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

// your output html

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

根據你的習慣來選擇,如果你不喜歡跨度很大的if(){},那用第二種方式是個好選擇

yangzie代碼結構

yangzie的目錄結構

yangzie 目錄是框架核心文件

scripts是構建腳本目錄

tests是單元測試文件目錄

tmp是其他一些臨時目錄

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

APP目錄詳細說明

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

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

接下來,開始寫代碼吧:《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不能訪問,則加上下面這句
    <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 一個恰到好處的小巧的PHP開發框架

yangzie是什么?

一個 PHP 框架;僅此而已。

你問它有什么優點?其實它的優點別的框架都有,它的缺點別的框架也有。

那為什么它有存在的價值?因為我不想重復:我不想在同一個項目中寫重復的代碼;我不想在不同的項目中寫重復的代碼;我不想年復一年的寫重復的代碼。

yangzie是一個輕便簡潔的php快速開發框架,采用MVC開發模式,基于module來開發組織功能;在M、V、C和module各方面都盡力做到重用

如何使用?

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

接下來

本文檔將解釋如何使用yangzie來開發,你只需根據我們的節奏一步一步走下去

了解一下《1.yangzie的目錄結構》,知道自己寫的代碼在哪里


為什么叫yangzie?

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