You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何为保密文件设置用户专属唯一下载权限?

安全的保密文件授权下载方案

嘿,这个需求很常见,我来给你理清楚思路,帮你搞定安全的授权下载方案!首先得明确:绝对不能把保密文件放在Web根目录下——这是你当前问题的核心,直接暴露文件路径导致任何人都能下载。下面分两种方案详细说,其中令牌授权的方案是首选,比创建唯一文件夹灵活得多。

方案一:唯一授权令牌+readfile()(推荐)

这个方案的核心是用数据库绑定「用户-文件-唯一令牌」,用户通过带令牌的请求访问中转脚本,脚本验证权限后用readfile()输出文件。优点是不复制文件、易管理权限/过期时间、安全性高。

具体步骤

  1. 迁移文件到Web根目录外:把PDF/DOCX文件移到Nginx/Apache无法直接访问的目录,比如/var/secure_files/,这样直接通过URL根本访问不到这些文件。
  2. 建数据库存储授权关系:创建一张表记录用户、文件路径、令牌和过期时间(可选)。
  3. 生成安全令牌:用加密安全的随机函数生成唯一字符串,避免用rand()这种不安全的函数。
  4. 分发令牌链接:把带令牌的下载链接发给用户(比如邮件、站内消息)。
  5. 中转脚本验证并输出文件:写一个PHP脚本(比如download.php),验证令牌有效性后,用readfile()输出文件。

示例代码

数据库表结构(MySQL)

CREATE TABLE file_authorizations (
    id INT AUTO_INCREMENT PRIMARY KEY,
    user_id INT NOT NULL, -- 关联你的用户表ID
    file_path VARCHAR(255) NOT NULL, -- 保密文件的绝对路径,比如/var/secure_files/my_file.pdf
    token VARCHAR(64) NOT NULL UNIQUE,
    expires_at DATETIME NULL, -- 可选,设置令牌过期时间(比如24小时)
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    downloaded_at DATETIME NULL -- 记录首次下载时间,防止重复使用
);

生成授权令牌的代码(用户申请下载时)

// 假设你已经获取了用户ID和要授权的文件路径
$user_id = 123;
$target_file = '/var/secure_files/confidential_report.docx';

// 生成加密安全的随机令牌(64位十六进制字符串)
$token = bin2hex(random_bytes(32));

// 插入数据库,设置24小时过期
$pdo = new PDO('mysql:host=localhost;dbname=your_database', 'db_user', 'db_password');
$stmt = $pdo->prepare(
    "INSERT INTO file_authorizations (user_id, file_path, token, expires_at) 
     VALUES (?, ?, ?, DATE_ADD(NOW(), INTERVAL 24 HOUR))"
);
$stmt->execute([$user_id, $target_file, $token]);

// 生成下载链接发给用户
$download_link = "https://yourdomain.com/download.php?token=" . urlencode($token);
echo "你的专属下载链接:" . $download_link;

下载验证脚本(download.php)

// 初始化数据库连接
$pdo = new PDO('mysql:host=localhost;dbname=your_database', 'db_user', 'db_password');

// 检查令牌是否存在
if (!isset($_GET['token'])) {
    http_response_code(403);
    die("请提供有效的授权令牌");
}

$token = $_GET['token'];

// 查询授权信息
$stmt = $pdo->prepare(
    "SELECT file_path, expires_at, downloaded_at 
     FROM file_authorizations 
     WHERE token = ?"
);
$stmt->execute([$token]);
$auth_info = $stmt->fetch(PDO::FETCH_ASSOC);

// 验证令牌有效性
if (!$auth_info) {
    http_response_code(403);
    die("无效的授权令牌");
}

// 检查是否过期
if ($auth_info['expires_at'] && strtotime($auth_info['expires_at']) < time()) {
    http_response_code(403);
    die("授权令牌已过期");
}

// 可选:禁止重复下载(根据需求调整)
if ($auth_info['downloaded_at']) {
    http_response_code(403);
    die("该链接已被使用");
}
// 标记为已下载
$stmt = $pdo->prepare("UPDATE file_authorizations SET downloaded_at = NOW() WHERE token = ?");
$stmt->execute([$token]);

$file_path = $auth_info['file_path'];

// 检查文件是否存在
if (!file_exists($file_path)) {
    http_response_code(404);
    die("文件不存在");
}

// 设置HTTP头,确保浏览器正确下载文件
$file_name = basename($file_path);
$file_size = filesize($file_path);

header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"" . $file_name . "\"");
header("Content-Length: " . $file_size);
header("Cache-Control: no-cache");

// 用readfile输出文件,它是高效的——直接从磁盘输出到浏览器,不需要把整个文件读入内存
readfile($file_path);
exit;

方案二:创建唯一用户文件夹(不推荐)

这个方案是给每个用户生成唯一命名的文件夹,把授权的文件复制进去,用户通过唯一文件夹路径访问。但缺点很明显:

  • 文件冗余:同一个文件可能被复制多次,浪费存储空间
  • 权限管理麻烦:需要确保文件夹权限正确,防止被其他用户访问
  • 过期处理繁琐:需要定时清理过期的文件夹和文件

如果一定要用这个方案,示例思路:

// 生成唯一文件夹名
$user_folder = '/var/www/uploads/' . bin2hex(random_bytes(16));
mkdir($user_folder, 0700, true); // 设置严格的权限,只有服务器能访问

// 复制文件到文件夹
copy('/var/secure_files/my_file.pdf', $user_folder . '/my_file.pdf');

// 生成下载链接
$download_link = "https://yourdomain.com/uploads/" . basename($user_folder) . "/my_file.pdf";

但再次强调:这个方案不适合保密文件,安全性和可维护性远不如令牌方案。

关键安全注意事项

  • 永远不要把保密文件放在Web根目录:这是最基础的安全要求
  • 用加密安全的随机函数生成令牌:比如random_bytes(),不要用rand()mt_rand()
  • 添加令牌过期时间:即使令牌泄露,也能限制被滥用的时间
  • 记录下载日志:方便追踪谁下载了什么文件
  • 设置正确的HTTP头:防止浏览器把文件解析成HTML(避免XSS风险)

内容的提问来源于stack exchange,提问作者john331

火山引擎 最新活动