tp5 auth權限的原理


我的一些個人理解,還是有些不懂的地方,有錯誤請指正,謝謝!!!

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() {
        if (config('auth_config')) {
            //可設置配置項 auth_config, 此配置項為數組。
            $this->_config = array_merge($this->_config, config('auth_config'));
            //不知道config('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;

        //小寫的組下全部權限名稱name(一維數組)
        $authList = $this->getAuthList($uid,$type); //獲取用戶需要驗證的所有有效規則列表   /*array( [0] =>controller/funtion,[1] => controller/funtion,[2]......   )  */

        //判斷name是什么類型,並 全部轉化成array類型
        if (is_string($name)) {
            $name = strtolower($name);//轉小寫
            if (strpos($name, ',') !== false) {//查找 "," 在字符串中第一次出現的位置:返回數字
                $name = explode(',', $name);//一維數組
            } else {
                $name = array($name);//一維數組
            }
//            $name = strpos($name, ',') !== false ? explode(',', $name) : [$name];
        }
        $list = array(); //保存驗證通過的規則名
        if ($mode=='url') {
            //serialize() 返回字符串,此字符串包含了表示 value 的字節流,可以存儲於任何地方。
            //strtolower:把所有字符轉換為小寫:
            //unserialize:從已存儲的表示中創建 PHP 的值
            $REQUEST = unserialize( strtolower(serialize($_REQUEST)) );
        }

        //兩種種比較方法!!!
        //if ($mode=='url' && $query!=$auth )     節點相符且url參數滿足
        //in_array($auth , $name)   name在auth存在對應

        foreach ( $authList as $auth ) {
            $query = preg_replace('/^.+\?/U','',$auth);
            if ($mode=='url' && $query!=$auth ) { //當賦予的權限和url上的路徑不一樣的時候的做法
                // //把查詢字符串解析到變量中:
                parse_str($query,$param); //解析規則中的param
                /*$param = array(1) {
                                         ["controller/index"] => string(0) ""
                                     }*/

                //array_intersect_assoc比較兩個數組的鍵名和鍵值,並返回交集:
                $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 ;
            }
        }

        //  or 和 and
        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 = \think\Db::name($this->_config['auth_group_access'])//用戶-用戶組關系表
            ->alias('a')
            ->join($this->_config['auth_group']." g", "g.id=a.group_id")//用戶組數據表名
            ->where("a.uid='$uid' and g.status='1'")
            ->field('uid,group_id,title,rules')->select();
        /*$user_groups = Db::view($this->config['auth_group_access'], 'uid,group_id')
                         ->view($this->config['auth_group'], 'title,rules',
                                "{$this->config['auth_group_access']}.group_id={$this->config['auth_group']}.id")
                        ->where(['uid' => $uid, 'status' => 1])->select();*/
        $groups[$uid] = $user_groups ? $user_groups : array();
        return $groups[$uid];
    }

    /**
     * 獲得權限列表
     * @param integer $uid  用戶id
     * @param integer $type 
     * @param $_SESSION['_auth_list_'.$uid.$t] = $_authList[$uid.$t] = 權限($name(array))一維數組 array( [0] =>controller/funtion,[1] => controller/funtion,[2]......   )
     */
    protected function getAuthList($uid,$type) {
        static $_authList = array(); //保存用戶驗證通過的權限列表

        //implode: 把數組元素組合為字符串
        $t = implode(',',(array)$type);//1

        if (isset($_authList[$uid.$t])) {//           已經獲取到權限name的 直接返回
            return $_authList[$uid.$t];
        }

        //登錄才需要用到的判斷
        if( $this->_config['auth_type']==2 && isset($_SESSION['_auth_list_'.$uid.$t])){//1 && _auth_list_241
            return $_SESSION['_auth_list_'.$uid.$t];
        }

        //讀取用戶所屬用戶組   'uid,group_id,title,rules(字符串,權限id)'二維數組,其實只要一組數據而已【0】
        $groups = $this->getGroups($uid);

        $ids = array();//保存用戶所屬用戶組設置的所有權限規則id
        foreach ($groups as $g) {
            //trim: 移除字符串左側的字符
            //explode: 把字符串打散為數組
            $ids = array_merge($ids, explode(',', trim($g['rules'], ',')));//一維數組
        }
        //array_unique: 移除數組中重復的值
        $ids = array_unique($ids);

        //如果沒有權限就返回一個空數組
        if (empty($ids)) {
            $_authList[$uid.$t] = array();
            return array();
        }

        $map=array(
            'id'=>array('in',$ids),
            'type'=>$type,
            'status'=>1,
        );
        //讀取用戶組所有權限規則
        $rules = \think\Db::name($this->_config['auth_rule'])->where($map)->field('condition,name')->select();//二維數組
           /*
            * array(3) {
                      [0] => array(2) {
                        ["condition"] => string(0) ""
                        ["name"] => string(8) "conf/add"
                      }
                      [1] => array(2) {
                        ["condition"] => string(0) ""
                        ["name"] => string(8) "conf/del"
                      }
                      [2] => array(2) {
                        ["condition"] => string(0) ""
                        ["name"] => string(8) "link/del"
                      }
                    }
            * */

        //循環規則,判斷結果。
        $authList = array();   //
        foreach ($rules as $rule) {
            if (!empty($rule['condition'])) { //根據condition進行驗證
                $user = $this->getUserInfo($uid);//獲取用戶全部信息,一維數組

                //preg_replace:替一個正則表達式的搜索和替換, 參數一:搜索,參數二:替換,參數三:要搜索替換的目標字符串或字符串數組
                //試了一下,不知道有什么用,$rule['condition']是什么,輸出的也一樣,沒變!
                //我覺得那個condition就是在這里用正則判斷的
                $command = preg_replace('/\{(\w*?)\}/', '$user[\'\\1\']', $rule['condition']);
                //dump($command);//debug
                //eval() 函數把字符串按照 PHP 代碼來計算。
                //$$command是什么,$condition就是什么,輸出的也一樣,沒變!
                @(eval('$condition=(' . $command . ');'));
                if ($condition) {
                    //strtolower: 把所有字符轉換為小寫:
                    $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]=\think\Db::name($this->_config['auth_user'])->where(array('uid'=>$uid))->find();
        }
        return $userinfo[$uid];
    }

}

對那個     登錄認證   還是不是很懂!!!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM