您好,欢迎访问一九零五行业门户网

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

接前文。 1.实现用户类cuser。 用户的存储采用文本形式,将用户数组进行json编码。 用户文件格式: * array ( * 'user1' = array ( * 'pass'='', * 'group'='', * 'home'='/home/ftp/', // ftp主目录 * 'active'= true , * 'expired='2015-12-12' , * 'descr
接前文。
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)时,则取消继承并为文件夹逐一授予权限。 *
实现代码如下:
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); }
其它类似信息

推荐信息