CodeIgniter 3迁移至Nginx后,如何实现仅允许已登录用户访问指定文件目录
兄弟,我完全懂你遇到的问题——把CI3从Apache迁到Nginx后,原本靠.htaccess做的权限控制直接失效了,未登录用户直接敲文件路径就能下载,你写的Nginx deny规则还不生效。这其实是Nginx的静态文件处理优先级导致的,咱们一步步来解决:
一、先搞懂为什么你的Nginx配置没生效
Nginx默认会优先处理真实存在的静态文件,也就是说,如果你的versoes或lgpd文件夹确实在网站根目录下,用户访问example.com.br/versoes/xyz.pdf时,Nginx会直接找到这个文件并返回,根本不会走你写的location /(versoes|lgpd)/规则。所以核心思路是:把这些文件请求先交给CodeIgniter做登录验证,再决定是否返回文件。
二、调整Nginx配置,让请求走CI路由
修改你的Nginx站点配置,把versoes和lgpd路径的请求转发到CI的入口文件index.php:
location ~ ^/(versoes|lgpd)/ { # 让Nginx不要直接处理静态文件,强制转发给CI处理 try_files $uri $uri/ /index.php$request_uri; }
这样不管文件是否存在,Nginx都会把请求交给CI,接下来就可以在CI里做权限校验了。
三、在CI里写一个权限验证+文件下载的控制器
创建一个Download.php控制器,专门处理文件下载的权限校验和文件输出:
<?php defined('BASEPATH') OR exit('No direct script access allowed'); class Download extends CI_Controller { public function __construct() { parent::__construct(); // 这里替换成你自己的登录校验逻辑 // 比如检查session里的登录状态标记 if (!$this->session->userdata('logged_in')) { // 未登录的话,要么返回403提示,要么跳转到登录页 show_error('你没有权限访问该资源', 403); // 或者用 redirect('login'); 跳转到登录页面 exit; } } // 处理文件下载请求 public function serve($folder, $filename) { // 建议把文件存放在网站根目录以外的安全路径,比如/var/www/private_files/ // 这里用FCPATH(CI的网站根目录常量)举例子,实际生产环境建议改更安全的路径 $file_real_path = FCPATH . $folder . '/' . $filename; // 先检查文件是否存在且可读 if (!file_exists($file_real_path) || !is_readable($file_real_path)) { show_error('请求的文件不存在或无法读取', 404); exit; } // 针对PDF文件设置响应头,让浏览器正确识别并触发下载 header('Content-Type: application/pdf'); header('Content-Disposition: attachment; filename="' . $filename . '"'); header('Content-Length: ' . filesize($file_real_path)); // 输出文件内容 readfile($file_real_path); exit; } }
四、配置CI路由,映射请求到控制器
打开application/config/routes.php,添加路由规则,把versoes和lgpd的请求指向刚才的控制器方法:
// 匹配versoes下的所有文件请求 $route['versoes/(:any)'] = 'download/serve/versoes/$1'; // 匹配lgpd下的所有文件请求 $route['lgpd/(:any)'] = 'download/serve/lgpd/$1';
五、进阶优化:用Nginx的X-Accel-Redirect提升下载效率
如果你的PDF文件比较大,用PHP的readfile可能会占用较多内存,推荐用Nginx的X-Accel-Redirect特性,让Nginx直接处理文件发送,减轻PHP的压力:
- 先把文件移到Nginx的内部访问路径,比如
/var/www/private_files/,然后在Nginx配置里添加一个内部location:
location /private_files/ { internal; # 只允许内部请求访问,外部直接访问会返回404 root /var/www; }
- 修改CI控制器的
serve方法:
public function serve($folder, $filename) { // 内部路径,对应Nginx里配置的/private_files/ $internal_path = '/private_files/' . $folder . '/' . $filename; // 真实文件路径,用于检查文件是否存在 $file_real_path = '/var/www/private_files/' . $folder . '/' . $filename; if (!file_exists($file_real_path) || !is_readable($file_real_path)) { show_error('请求的文件不存在或无法读取', 404); exit; } // 设置响应头,让Nginx接管文件发送 header('Content-Type: application/pdf'); header('Content-Disposition: attachment; filename="' . $filename . '"'); header('X-Accel-Redirect: ' . $internal_path); exit; }
这样配置后,只有登录用户才能通过CI的验证触发Nginx的文件发送,未登录用户直接访问文件路径会被Nginx拦截或者CI返回403,完美解决你的问题!
备注:内容来源于stack exchange,提问作者Zanellato

