PHP阻止直接访问文件,但只允许登录文件的所有者

时间:2014-04-19 20:20:36

标签: php mysql .htaccess access-control

编辑:downl.php

 嗨Royal Bg。

我不知道该怎么感谢你。但是你的代码似乎很好但很难让我理解,所以我仍然推荐下载页面.... 我的downl.php页面如下所示:

 require('staff.php'); // User controll


if(($thisuser->getGroupId() != 4)){
    header('Location: index.php');
    require('index.php');  
    exit;
}

//收到学生文件ID,学生ID和朗姆酒ID(课程ID)来自rum_st.php

if (isset($_GET['f-id']) && is_numeric($_GET['f-id']) && 
 $_GET['f-id'] >= 1 && isset($_GET['s-id']) && is_numeric($_GET['s-id']) && 
 $_GET['s-id'] >= 1 && isset($_GET['r-id']) && is_numeric($_GET['r-id']) && $_GET['r-id'] >= 1 ) {


 $file_id_s = $_GET['f-id'];
$stud_id = $_GET['s-id'];
$rums_id = $_GET['r-id'];



     $sql = " SELECT t.ticket_id, t.ticketID, t.staff_id, t.subject, r.rums_id, r.staff_id, r.rumsname ".
            " f.file1_id, f.rums_id, f.ticket_id, f.file_name, f.staff_id, f.filetype, f.filesize, f.created, f.dept_id ". // dept_id means class_id or grade_id
            " sc.school_id, sc.school_name ".
            " FROM ice_cust_ticket t ".
            " INNER JOIN ice_file f ON f.ticket_id = t.ticket_id ".

            " INNER JOIN ice_rums r ON r.rums_id = f.rums_id ".
            " INNER JOIN ice_cust_school sc ON sc.school_id = f.dept_id ". // school means class or grade

            " WHERE f.rums_id = ".$rums_id." " .
            " ORDER by f.created ";


     $res = mysql_query($sql);


 while ($row = mysql_fetch_array($res)) {

    $filename = htmlspecialchars($row['file_name']);
    $fileID = $row['file1_id'];
    $rums_id3 = $row['rums_id'];
    $dept_id = $row['dept_id'];  // Class id if he/she is from class 4 .. 5 ..
    $dept_id3 = explode(',', $dept_id);

    if(in_array($thisuser->getdeptID(), $dept_id3)){


    $rumsname =  htmlentities($row['rumsname'], ENT_QUOTES, "UTF-8");
    $file_name =  htmlentities($row['file_name'], ENT_QUOTES, "UTF-8");
    $teacher_id = $row['staff_id'];

    // Teacher file
   // $target_path = "rums/".$teacher_id."/".$rumsname."/".$file_name."/";

   // Student file
    $target_path_st = "rums/".$teacher_id."/".$rumsname."/".$file_name."/".$thisuser->getId()."/";



    $sql_st_file = " SELECT * FROM ice_student_file WHERE file_id = ".$fileID." AND stud_id = ".$thisuser->getId()." ";
    $res_st = mysql_query($sql_st_file);
    $row_st = mysql_fetch_array($res_st);
    $file = $row_st['file_name'];
    $result = $row_st['result'];
    $opinion = $row_st['opinion'];


     // with link like target=_blank......
 //$url =  '<a href="'.$target_path_st.$file.'" target="_blank id="attached-file-link" style="text-decoration:none;">'.$file.'</a>'; 

   // or without link
   $url = '<a href="'.$target_path_st.$file.'">'.$file.'</a>';

   // this give me the same problem.. shows the link where it is and I have to click to download
   //echo $url;

   // echo '<META HTTP-EQUIV=Refresh CONTENT="0; URL='.$url.'">';  // And This give me Error 403

    // The question is where to put the headers and what to write on headers...


    } 

    } 

    }


我正在用PHP为我的学校创建一个简单的系统。 教师在名为rums的文件夹中创建自己的课程 他们可以将这些课程提供给不同的课程。 例如,Linda老师创建了名为Biology的课程,并提供了对4级和5级的访问。它运行正常。

所以这将是localhost \ my \ my2 \ rums \ ...并在这里创建名为Biology的文件夹。但是在Biology之前,它还创建了一个文件夹,用户ID为nr,例如Linda用户ID为91只是为了防止与其他教师课程名称发生冲突。所以它会像下面这样: localhost \ my \ my2 \ rums \ 91 \ Biology

Linda将可下载的文件(如docx和pdf文件)插入到此Biology文件夹中,来自4级和5级的学生可以下载。然后他们可以将不同的文件插入Linda到这个文件夹作为对作业的回应,这样她也可以下载学生文件。 这个php页面只能访问那些有权限的类。它工作正常,他们可以看到教师档案。

所以,如果学生我们说她的名字是Sarah,从4级插入文件到这个文件夹,他就能看到她自己插入的文件..而且它工作正常。没有其他人可以看到它。 从数据库我得到这些文件:

    $target_path_teacher_file = "rums/".$teacher_id."/".$rumsname."/".$file_name."/";
    $target_path_student_file = "rums/".$teacher_id."/".$rumsname."/".$file_name."/".$thisuser->getId

()."/";

要在桌子上展示我打印出来就像这样:

<td><center><?php print '<a href="'.$ target_path_teacher_file.$filename.'" target="_blank id="attached-file-link" style="text-decoration:none;">'.$filename.'</a>'; ?></center></td>
<td><center><?php print '<a href="'.$target_path_student_file.$file.'" target="_blank id="attached-file-link" style="text-decoration:none;">'.$file.'</a>'; ?></center></td>



但是在文件上你可以看到文件所在的空洞链接..如果大卫复制此链接并发送给其他人并通过网络浏览器过去,那么即使他们也可以下载他的文件和教师文件。

亲爱的人们......我的问题是如何防止其他已注册的人员下载,除Sarah外没有注册人员?我尝试使用.htaccess文件,但没有工作。

任何帮助你都会非常感激。
/乔纳斯

enter image description here

1 个答案:

答案 0 :(得分:1)

我可以想象的第一眼看到的解决方案是将文件存储到非公共目录中:

即:

www/private/assignment.docx

当您验证用户凭据时,即Sarah来自第4类并且其中一个请求该文件,请在后台运行一个脚本,该脚本将该文件的临时副本复制到具有不同名称的公共目录,即

www/public/sarah-homework-2014-04-20.docx

和/或也没有给他们机会知道文件的来源。或者至少让它变得更难。您不需要直接链接。通过会话download.phpuser_id发送您的链接指向file_id

如果此user_id具有权限并从download.php直接开始下载

,则在download.php中验证

您也不能允许下载,但会向他们发送带有该文件的电子邮件。在成功验证用户来自允许的类之后,可以使用附加的文件接收电子邮件。


我能想象的所有其他方式都是文件系统权限,.htaccess等等。


关于评论中的问题。

我建议通过设置正确的内容类型标题来使用下载。 将文件作为资源打开,然后强制下载。

一个简单的用户类,你有userId,所以你可以检查权限(我不会在这里检查权限,它只是一个例子)

class User {
    private $_id;
    public function setId($id) { $this->_id = $id; }
    public function getId() { return $this->_id; }
}

收到file_id后获取文件信息的简单类;

class File {

    private $_directory;
    private $_filename;
    private $_id;

    public function setId($file_id) { 
        $this->_id = $file_id; 
        $this->getFileInfo();
        return $this;
    }

    private function getFileInfo() {
        $result = $this->getDb()->query("SELECT directory, filename FROM files WHERE id = {$this->getId()};");
        $row = $this->getDb()->fetch($result);
        $this->_directory = $row['directory'];
        $this->_filename = $row['filename'];
    }

    public function getDirectory() { return $this->_directory; }
    public function getFileName() { return $this->_filename; }
    public function getId() { return $this->_id; }

}

然后是下载课程。下载类接收$ user和$ file的实例。设置属性file_id和user_id后,使用这些实例调用下载类

class Download {

    private $_path;

    /**
     * @var File
     */
    private $_file;
    /**
     * @var User
     */
    private $_user;
    /**
     * @var Download
     */
    private $_size, $_info, $_ext, $_handle;
    private static $_inst = null;

    /**
     * 
     * @param User $user
     * @param File $file
     * @return Download
     */
    public static function init(User $user, File $file) {
        if (self::$_inst == null) {
            self::$_inst = new self();
            self::$_inst->_file = $file;
            self::$_inst->_user = $user;
            self::$_inst->setPath();
        }
        return self::$_inst;
    }
    /**
     * @return File
     */
    public function getFile() { return $this->_file; }
    /**
     *  @return User
     */
    public function getUser() { return $this->_user; }

    private function setPath() {
        $this->_path = $this->getFile()->getDirectory() . DIRECTORY_SEPARATOR . $this->getFile()->getFileName();
    }

    public function getPath() { return __DIR__.DIRECTORY_SEPARATOR.$this->_path; }

    private function setFileInfo() {
        $this->_handle = fopen($this->getPath(), 'r');
            $this->_size = filesize($this->getPath());
            $this->_info = pathinfo($this->getPath());
            $this->_ext = strtolower($this->_info['extension']);

    }

    private function setHeaders() {
        switch ($this->_ext) {
            case '.docx':
                header("Content-type: application/vnd.openxmlformats-officedocument.wordprocessingml.document");
                header("Content-Disposition: attachment; filename=\"".$this->_info['basename']."\"");
                break;
            case '.doc':
                header("Content-type: application/msword");
                header("Content-Disposition: attachment; filename=\"".$this->_info['basename']."\"");
                break;
            case '.txt':
                header("Content-Type:text/plain");
                header("Content-Disposition: attachment; filename=\"".$this->_info['basename']."\"");
                break;
            default;
                header("Content-type: application/octet-stream");
                header("Content-Disposition: filename=\"".$this->_info['basename']."\"");
        }
        header("Content-length: $this->_size");
        header("Cache-control: private");
    }

    public function forceDownload() {
        $this->setFileInfo();
        $this->setHeaders();
        while(!feof($this->_handle)) {
            fread($this->_handle, 2048);
        }
        fclose($this->_handle);
        return true;
    }

}

然后你需要像

这样的东西
$user = new User();
$file = new File;

$user->setId(8);
$file->setId(3);
Download::init($user, $file)->forceDownload();

您也不能回显文件的基本名称,但也不能回复其他名称,因此您不会透露其名称