欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  后端开发

用PHP实现一个高效率安全的ftp服务器(二)

程序员文章站 2022-06-06 21:49:40
...
用PHP实现一个高效安全的ftp服务器(二)

接前文。

 1.实现用户类CUser。

  用户的存储采用文本形式,将用户数组进行json编码。  

用户文件格式: * array( *         'user1' => array( *             'pass'=>'', *             'group'=>'', *             'home'=>'/home/ftp/', //ftp主目录 *             'active'=>true, *             'expired=>'2015-12-12', *             'description'=>'', *             'email' => '', *             'folder'=>array( *                     //可以列出主目录下的文件和目录,但不能创建和删除,也不能进入主目录下的目录 *                     //前1-5位是文件权限,6-9是文件夹权限,10是否继承(inherit) *                     array('path'=>'/home/ftp/','access'=>'RWANDLCNDI'), *                     //可以列出/home/ftp/a/下的文件和目录,可以创建和删除,可以进入/home/ftp/a/下的子目录,可以创建和删除。 *                     array('path'=>'/home/ftp/a/','access'=>'RWAND-----'), *             ), *             'ip'=>array( *                 'allow'=>array(ip1,ip2,...),//支持*通配符: 192.168.0.* *                 'deny'=>array(ip1,ip2,...) *             ) *         )  * ) *  * 组文件格式: * array( *         'group1'=>array( *             'home'=>'/home/ftp/dept1/', *             'folder'=>array( *  *             ), *             'ip'=>array( *                 'allow'=>array(ip1,ip2,...), *                 'deny'=>array(ip1,ip2,...) *            ) *         ) * )

  文件夹和文件的权限说明:

 * 文件权限  * R读 : 允许用户读取(即下载)文件。该权限不允许用户列出目录内容,执行该操作需要列表权限。  * W写: 允许用户写入(即上传)文件。该权限不允许用户修改现有的文件,执行该操作需要追加权限。 * A追加: 允许用户向现有文件中追加数据。该权限通常用于使用户能够对部分上传的文件进行续传。  * N重命名: 允许用户重命名现有的文件。 * D删除: 允许用户删除文件。  *  * 目录权限  * L列表: 允许用户列出目录中包含的文件。 * C创建: 允许用户在目录中新建子目录。  * N重命名: 允许用户在目录中重命名现有子目录。 * D删除: 允许用户在目录中删除现有子目录。注意: 如果目录包含文件,用户要删除目录还需要具有删除文件权限。 *  * 子目录权限 * I继承: 允许所有子目录继承其父目录具有的相同权限。继承权限适用于大多数情况,但是如果访问必须受限于子文件夹,例如实施强制访问控制(Mandatory Access Control)时,则取消继承并为文件夹逐一授予权限。 * 

  实现代码如下:  

  

用PHP实现一个高效率安全的ftp服务器(二)
class User{        const I = 1;    // inherit        const FD = 2;    // folder delete        const FN = 4;    // folder rename        const FC = 8;    // folder create        const FL = 16;    // folder list        const D = 32;    // file delete        const N = 64;    // file rename        const A = 128;    // file append        const W = 256;    // file write (upload)        const R = 512;    // file read (download)            private $hash_salt = '';        private $user_file;        private $group_file;        private $users = array();        private $groups = array();        private $file_hash = '';         public function __construct(){        $this->user_file = BASE_PATH.'/conf/users';        $this->group_file = BASE_PATH.'/conf/groups';        $this->reload();    }        /**     * 返回权限表达式     * @param int $access     * @return string     */    public static function AC($access){        $str = '';        $char = array('R','W','A','N','D','L','C','N','D','I');        for($i = 0; $i $i++){            if($access & pow(2,9-$i))$str.= $char[$i];else $str.= '-';        }        return $str;    }        /**     * 加载用户数据     */    public function reload(){        $user_file_hash = md5_file($this->user_file);        $group_file_hash = md5_file($this->group_file);                if($this->file_hash != md5($user_file_hash.$group_file_hash)){            if(($user = file_get_contents($this->user_file)) !== false){                $this->users = json_decode($user,true);                if($this->users){                    //folder排序                    foreach ($this->users as $user=>$profile){                        if(isset($profile['folder'])){                            $this->users[$user]['folder'] = $this->sortFolder($profile['folder']);                        }                    }                }            }            if(($group = file_get_contents($this->group_file)) !== false){                $this->groups = json_decode($group,true);                if($this->groups){                    //folder排序                    foreach ($this->groups as $group=>$profile){                                                    if(isset($profile['folder'])){                                                    $this->groups[$group]['folder'] = $this->sortFolder($profile['folder']);                        }                    }                }            }            $this->file_hash = md5($user_file_hash.$group_file_hash);                    }    }        /**     * 对folder进行排序     * @return array     */    private function sortFolder($folder){        uasort($folder, function($a,$b){            return strnatcmp($a['path'], $b['path']);        });            $result = array();        foreach ($folder as $v){            $result[] = $v;        }            return $result;    }        /**     * 保存用户数据     */    public function save(){        file_put_contents($this->user_file, json_encode($this->users),LOCK_EX);        file_put_contents($this->group_file, json_encode($this->groups),LOCK_EX);    }        /**     * 添加用户     * @param string $user     * @param string $pass     * @param string $home     * @param string $expired     * @param boolean $active     * @param string $group     * @param string $description     * @param string $email     * @return boolean     */    public function addUser($user,$pass,$home,$expired,$active=true,$group='',$description='',$email = ''){        $user = strtolower($user);        if(isset($this->users[$user]) || empty($user)){            return false;        }                $this->users[$user] = array(                'pass' => md5($user.$this->hash_salt.$pass),                'home' => $home,                'expired' => $expired,                'active' => $active,                'group' => $group,                'description' => $description,                'email' => $email,        );        return true;    }        /**     * 设置用户资料     * @param string $user     * @param array $profile     * @return boolean     */    public function setUserProfile($user,$profile){        $user = strtolower($user);        if(is_array($profile) && isset($this->users[$user])){            if(isset($profile['pass'])){                $profile['pass'] = md5($user.$this->hash_salt.$profile['pass']);            }            if(isset($profile['active'])){                if(!is_bool($profile['active'])){                    $profile['active'] = $profile['active'] == 'true' ? true : false;                }            }                        $this->users[$user] = array_merge($this->users[$user],$profile);            return true;        }        return false;    }        /**     * 获取用户资料     * @param string $user     * @return multitype:|boolean     */    public function getUserProfile($user){        $user = strtolower($user);        if(isset($this->users[$user])){            return $this->users[$user];        }        return false;    }    /**     * 删除用户     * @param string $user     * @return boolean     */    public function delUser($user){        $user = strtolower($user);        if(isset($this->users[$user])){            unset($this->users[$user]);            return true;        }        return false;    }        /**     * 获取用户列表     * @return array     */    public function getUserList(){        $list = array();        if($this->users){            foreach ($this->users as $user=>$profile){                $list[] = $user;            }        }        sort($list);        return $list;    }        /**     * 添加组     * @param string $group     * @param string $home     * @return boolean     */    public function addGroup($group,$home){        $group = strtolower($group);        if(isset($this->groups[$group])){            return false;        }        $this->groups[$group] = array(                'home' => $home        );        return true;    }        /**     * 设置组资料     * @param string $group     * @param array $profile     * @return boolean     */    public function setGroupProfile($group,$profile){        $group = strtolower($group);        if(is_array($profile) && isset($this->groups[$group])){            $this->groups[$group] = array_merge($this->groups[$group],$profile);            return true;        }        return false;    }        /**     * 获取组资料     * @param string $group     * @return multitype:|boolean     */    public function getGroupProfile($group){        $group = strtolower($group);        if(isset($this->groups[$group])){            return $this->groups[$group];        }        return false;    }        /**     * 删除组     * @param string $group     * @return boolean     */    public function delGroup($group){        $group = strtolower($group);        if(isset($this->groups[$group])){            unset($this->groups[$group]);            foreach ($this->users as $user => $profile){                if($profile['group'] == $group)                    $this->users[$user]['group'] = '';            }            return true;        }        return false;    }        /**     * 获取组列表     * @return array     */    public function getGroupList(){        $list = array();        if($this->groups){            foreach ($this->groups as $group=>$profile){                $list[] = $group;            }        }        sort($list);        return $list;    }        /**     * 获取组用户列表     * @param string $group     * @return array     */    public function getUserListOfGroup($group){        $list = array();        if(isset($this->groups[$group]) && $this->users){            foreach ($this->users as $user=>$profile){                if(isset($profile['group']) && $profile['group'] == $group){                    $list[] = $user;                }            }        }        sort($list);        return $list;    }        /**     * 用户验证     * @param string $user     * @param string $pass     * @param string $ip     * @return boolean     */    public function checkUser($user,$pass,$ip = ''){        $this->reload();        $user = strtolower($user);        if(isset($this->users[$user])){            if($this->users[$user]['active'] && time() strtotime($this->users[$user]['expired'])                 && $this->users[$user]['pass'] == md5($user.$this->hash_salt.$pass)){                if(empty($ip)){                    return true;                }else{                    //ip验证                    return $this->checkIP($user, $ip);                }            }else{                return false;            }                }        return false;    }        /**     * basic auth      * @param string $base64         */    public function checkUserBasicAuth($base64){        $base64 = trim(str_replace('Basic ', '', $base64));        $str = base64_decode($base64);        if($str !== false){            list($user,$pass) = explode(':', $str,2);            $this->reload();            $user = strtolower($user);            if(isset($this->users[$user])){                $group = $this->users[$user]['group'];                if($group == 'admin' && $this->users[$user]['active'] && time() strtotime($this->users[$user]['expired'])                && $this->users[$user]['pass'] == md5($user.$this->hash_salt.$pass)){                                    return true;                }else{                    return false;                }            }        }        return false;    }        /**     * 用户登录ip验证     * @param string $user     * @param string $ip     *      * 用户的ip权限继承组的IP权限。     * 匹配规则:     * 1.进行组允许列表匹配;     * 2.如同通过,进行组拒绝列表匹配;     * 3.进行用户允许匹配     * 4.如果通过,进行用户拒绝匹配     *      */    public function checkIP($user,$ip){        $pass = false;        //先进行组验证                $group = $this->users[$user]['group'];        //组允许匹配        if(isset($this->groups[$group]['ip']['allow'])){            foreach ($this->groups[$group]['ip']['allow'] as $addr){                $pattern = '/'.str_replace('*','\d+',str_replace('.', '\.', $addr)).'/';                if(preg_match($pattern, $ip) && !empty($addr)){                    $pass = true;                    break;                }            }        }        //如果允许通过,进行拒绝匹配        if($pass){            if(isset($this->groups[$group]['ip']['deny'])){                foreach ($this->groups[$group]['ip']['deny'] as $addr){                    $pattern = '/'.str_replace('*','\d+',str_replace('.', '\.', $addr)).'/';                    if(preg_match($pattern, $ip) && !empty($addr)){                        $pass = false;                        break;                    }                }            }        }                if(isset($this->users[$user]['ip']['allow'])){                        foreach ($this->users[$user]['ip']['allow'] as $addr){                $pattern = '/'.str_replace('*','\d+',str_replace('.', '\.', $addr)).'/';                if(preg_match($pattern, $ip) && !empty($addr)){                    $pass = true;                    break;                }            }        }        if($pass){            if(isset($this->users[$user]['ip']['deny'])){                foreach ($this->users[$user]['ip']['deny'] as $addr){                    $pattern = '/'.str_replace('*','\d+',str_replace('.', '\.', $addr)).'/';                    if(preg_match($pattern, $ip) && !empty($addr)){                        $pass = false;                        break;                    }                }            }        }        echo date('Y-m-d H:i:s')." [debug]\tIP ACCESS:".' '.($pass?'true':'false')."\n";        return $pass;    }        /**     * 获取用户主目录     * @param string $user     * @return string     */    public function getHomeDir($user){        $user = strtolower($user);        $group = $this->users[$user]['group'];        $dir = '';        if($group){            if(isset($this->groups[$group]['home']))$dir = $this->groups[$group]['home'];        }        $dir = !empty($this->users[$user]['home'])?$this->users[$user]['home']:$dir;        return $dir;    }        //文件权限判断    public function isReadable($user,$path){                $result = $this->getPathAccess($user, $path);        if($result['isExactMatch']){            return $result['access'][0] == 'R';        }else{            return $result['access'][0] == 'R' && $result['access'][9] == 'I';        }    }            public function isWritable($user,$path){                $result = $this->getPathAccess($user, $path);                if($result['isExactMatch']){            return $result['access'][1] == 'W';        }else{            return $result['access'][1] == 'W' && $result['access'][9] == 'I';        }    }        public function isAppendable($user,$path){        $result = $this->getPathAccess($user, $path);        if($result['isExactMatch']){            return $result['access'][2] == 'A';        }else{            return $result['access'][2] == 'A' && $result['access'][9] == 'I';        }    }            public function isRenamable($user,$path){        $result = $this->getPathAccess($user, $path);        if($result['isExactMatch']){            return $result['access'][3] == 'N';        }else{            return $result['access'][3] == 'N' && $result['access'][9] == 'I';        }    }    public function isDeletable($user,$path){                $result = $this->getPathAccess($user, $path);        if($result['isExactMatch']){            return $result['access'][4] == 'D';        }else{            return $result['access'][4] == 'D' && $result['access'][9] == 'I';        }    }        //目录权限判断    public function isFolderListable($user,$path){        $result = $this->getPathAccess($user, $path);        if($result['isExactMatch']){            return $result['access'][5] == 'L';        }else{            return $result['access'][5] == 'L' && $result['access'][9] == 'I';        }    }        public function isFolderCreatable($user,$path){        $result = $this->getPathAccess($user, $path);        if($result['isExactMatch']){            return  $result['access'][6] == 'C';        }else{            return  $result['access'][6] == 'C' && $result['access'][9] == 'I';        }    }        public function isFolderRenamable($user,$path){        $result = $this->getPathAccess($user, $path);        if($result['isExactMatch']){            return $result['access'][7] == 'N';        }else{            return $result['access'][7] == 'N' && $result['access'][9] == 'I';        }    }        public function isFolderDeletable($user,$path){        $result = $this->getPathAccess($user, $path);        if($result['isExactMatch']){            return $result['access'][8] == 'D';        }else{            return $result['access'][8] == 'D' && $result['access'][9] == 'I';        }    }        /**     * 获取目录权限     * @param string $user     * @param string $path     * @return array     * 进行最长路径匹配     *      * 返回:     * array(     * 'access'=>目前权限         *    ,'isExactMatch'=>是否精确匹配     *         * );     *      * 如果精确匹配,则忽略inherit.     * 否则应判断是否继承父目录的权限,     * 权限位表:     * +---+---+---+---+---+---+---+---+---+---+     * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |     * +---+---+---+---+---+---+---+---+---+---+     * | R | W | A | N | D | L | C | N | D | I |     * +---+---+---+---+---+---+---+---+---+---+     * |       FILE        |     FOLDER        |     * +-------------------+-------------------+     */        public function getPathAccess($user,$path){        $this->reload();        $user = strtolower($user);        $group = $this->users[$user]['group'];                //去除文件名称        $path = str_replace(substr(strrchr($path, '/'),1),'',$path);        $access = self::AC(0);                $isExactMatch = false;        if($group){            if(isset($this->groups[$group]['folder'])){                                foreach ($this->groups[$group]['folder'] as $f){                    //中文处理                    $t_path = iconv('UTF-8','GB18030',$f['path']);                                        if(strpos($path, $t_path) === 0){                        $access = $f['access'];                                                $isExactMatch = ($path == $t_path?true:false);                    }                                        }            }        }        if(isset($this->users[$user]['folder'])){            foreach ($this->users[$user]['folder'] as $f){                //中文处理                $t_path用PHP实现一个高效率安全的ftp服务器(二)

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。

相关文章

相关视频


网友评论

文明上网理性发言,请遵守 新闻评论服务协议

我要评论
  • 用PHP实现一个高效率安全的ftp服务器(二)
  • 专题推荐

    作者信息
    用PHP实现一个高效率安全的ftp服务器(二)

    认证0级讲师

    推荐视频教程
  • 用PHP实现一个高效率安全的ftp服务器(二)javascript初级视频教程
  • 用PHP实现一个高效率安全的ftp服务器(二)jquery 基础视频教程
  • 视频教程分类