php 8.1 中“上传文件下载”需手动实现权限控制:文件存于 web 根目录外私有路径,下载前校验登录态、数据库归属及物理路径合法性,并配合 web 服务器禁止直接访问私有目录。

PHP 8.1 中“上传文件下载”本身不涉及自动的下载权限控制——上传和下载是两个独立动作。所谓“设置下载权限控制”,实际是指:当用户请求下载某个已上传的文件时,后端必须校验该用户是否有权访问该文件,再决定是否允许下载。这不是靠 PHP 配置开关实现的,而是靠代码逻辑+目录权限+Web 服务器规则共同完成。
下面分三块讲清楚怎么做:
文件存储位置不能直接被 Web 访问
把用户上传的文件(如头像、合同、附件)放到 Web 根目录(如 /var/www/html/uploads/)下,等于对外公开。任何知道 URL 的人都能直接下载,毫无权限可言。
正确做法是:
- 将上传文件存到 Web 根目录之外 的私有目录,例如:
/var/www/private_uploads/2026/09/user123/report.pdf - 确保该目录 不被 Apache/Nginx 直接映射为可访问路径(即没有
Alias或location指向它)。 - 在 PHP 脚本中通过
readfile()或fpassthru()输出文件内容,但前提是先完成权限判断。
下载前必须做三重校验
每次用户发起下载请求(比如访问 /download.php?id=456),PHP 脚本需依次检查:
- 用户是否已登录(检查 session 或 token);
- 该文件 ID 对应的记录是否真实存在且属于当前用户(查数据库:
SELECT * FROM uploads WHERE id = ? AND user_id = ?); - 文件物理路径是否合法、未越界(用
realpath()校验路径是否落在/var/www/private_uploads/内,防止../../../etc/passwd类路径遍历)。
示例逻辑片段:
$upload = $pdo->prepare("SELECT path, filename FROM uploads WHERE id = ? AND user_id = ?")->execute([$id, $_SESSION['user_id']])->fetch();
if (!$upload) {
http_response_code(403);
exit('无权下载');
}
$fullPath = '/var/www/private_uploads/' . ltrim($upload['path'], '/');
if (strpos(realpath($fullPath), '/var/www/private_uploads/') !== 0) {
http_response_code(400);
exit('非法路径');
}
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $upload['filename'] . '"');
readfile($fullPath);
Web 服务器层加一层防护(推荐)
即使 PHP 做了校验,仍建议配合 Nginx 或 Apache 限制对私有目录的直接访问:
-
Nginx 示例(在 server 块中添加):
location ^~ /private_uploads/ { deny all; } -
Apache 示例(在
.htaccess或虚拟主机配置中):<directory> Require all denied </directory>
这样,哪怕有人猜到路径,也会被 Web 服务器直接拦截,根本到不了 PHP。
不复杂但容易忽略。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











