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

标签 php mysql .htaccess access-control

已编辑:downl.php

嗨皇家 Bg。

不知该如何感谢。但是你的代码看起来不错,但对我来说很难
理解所以我仍然引用传递到下载页面.... 我的 downl.php 页面如下所示:

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


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

//从 rum_st.php 接收学生文件 id、学生 id 和 rums id(类(class) id)

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 的文件夹中创建自己的类(class) 他们可以将这些类(class)授予不同类(class)的访问权限。 例如,Linda 老师创建了名为 Biology 的类(class),并授予了 4 类和 5 类的访问权限。效果很好。

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

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

因此,如果学生我们说她的名字是来自第 4 类的 Sarah 将文件插入到此文件夹中,他将只能看到她自己插入的文件..并且它工作正常。没有其他人可以看到它。 从数据库中我得到那些文件:

    $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>



但是将鼠标悬停在文件上,您可以看到文件所在的空洞链接……如果 David 复制此链接并发送给其他人并在网络浏览器上粘贴,那么即使他们也可以下载他的文件和教师文件。

所以我亲爱的人..我的问题是如何防止除莎拉以外的其他注册和未注册人员下载?我尝试使用 .htaccess 文件但没有成功。

您能提供的任何帮助将不胜感激。
/乔纳斯

enter image description here

最佳答案

乍一看,我可以想象的解决方案是将文件存储到非公共(public)目录中,无需复杂的目录权限:

即:

www/private/assignment.docx

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

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

和/或也不让他们有机会知道文件的来源。或者至少让它变得更难。您不需要直接链接到它。让您的链接指向 download.php,并通过 session user_idfile_id 发送。

如果此 user_id 具有权限,则在 download.php 中验证并直接从 download.php 开始下载


您也可以不允许下载,但将文件与电子邮件一起发送给他们。成功验证用户来自允许的类别后,可以收到带有附件的电子邮件。


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


关于评论中的问题。

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

一个简单的用户类,你有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();

你也可以不回显文件的基本名称,而是其他的东西,所以你也不会透露它的名字

关于PHP 阻止直接访问文件但只允许登录文件的所有者,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23175058/

相关文章:

mysql - 如何为多个用户使用单个表

mysql - 在一个查询中计算来自不同表的结果

mysql - InnoDB 行大小在表增长时呈指数变化?

php - 关于 PHP 语法错误的 Apache 内部错误 500 (htaccess)

php - 如何在 Laravel 5 中从 Carbon 获取当前时间戳

javascript - Php magento SESSION 变量在 php 内执行 javascript 代码之前未设置

php - json 字符串化为 php

.htaccess - .htaccess 中的 MIME 类型带或不带前导点

php - htaccess 允许访问一个 PHP 文件

javascript 中的 php 计数器不起作用