CodeIgniter4多租户应用如何访问public目录外的公共资源文件
CodeIgniter4 多租户架构下共享公共静态资源实现方案
核心原则:公共资源只存一份,不重复复制到每个租户目录,优先选择无PHP开销的直出方案。
方案1:Web服务器直出(性能最优,生产环境推荐)
静态资源直接由Nginx/Apache返回,不经过CI4框架解析,性能是PHP转发方案的5~10倍,适合无特殊权限要求的JS/CSS/图片等公共资源。
- 第一步:创建公共资源存储目录
在所有租户目录的同级位置(即域名映射的网站根目录,和test、test1租户文件夹平级)新建shared_assets目录,按资源类型分子目录存放文件,最终目录结构如下:网站根目录/ ├── test/ # test租户的原public目录内容 ├── test1/ # test1租户的原public目录内容 └── shared_assets/ # 公共资源根目录 ├── css/ # 公共样式文件 ├── js/ # 公共脚本文件 └── img/ # 公共图片文件 - 第二步:配置Web服务器规则
配置规则的作用是:无论当前访问的是哪个租户路径,所有指向公共资源的请求都会被统一映射到上述公共目录,不会走到租户的入口文件。
Apache环境:在网站根目录新建.htaccess文件,写入以下规则:
Nginx环境:在域名对应的server配置块中加入以下规则,重载Nginx配置即可生效:RewriteEngine On # 统一拦截所有路径下的公共资源请求,映射到根目录的公共资源文件夹 RewriteRule ^(.*)shared_assets/(.*)$ /shared_assets/$2 [L] # 禁止访问敏感配置文件 <FilesMatch "\.(htaccess|htpasswd|ini|env)"> Order Allow,Deny Deny from all </FilesMatch>location ~* /shared_assets/ { root /网站根目录的绝对路径/; expires 30d; # 开启30天浏览器缓存,可选 access_log off; add_header Cache-Control "public, max-age=2592000"; } - 第三步:视图中引用资源
直接使用绝对根路径引用即可,不受当前租户路径影响:
为了方便后续路径调整,可以在<!-- 公共CSS --> <link rel="stylesheet" href="/shared_assets/css/common.css"> <!-- 公共JS --> <script src="/shared_assets/js/common.js"></script> <!-- 公共图片 --> <img src="/shared_assets/img/site-logo.png" alt="站点logo">app/Config/Constants.php中定义全局常量:
视图中用常量拼接路径即可,后续调整公共资源路径只需要修改这一处配置。define('SHARED_ASSETS_URL', '/shared_assets/');
方案2:CI4路由转发(适合有权限控制的场景)
如果部分公共资源需要登录校验、租户权限判断后才能访问,可以用这个方案,所有资源请求经过CI4框架处理,灵活度高但性能稍差。
- 第一步:创建公共资源存储目录
在CI4项目根目录(和app、system目录平级,不在任何租户的public目录内)新建shared_assets目录,按类型存放公共资源。 - 第二步:新建资源控制器
在app/Controllers目录下创建SharedAssets.php控制器,注意加白名单和路径过滤,防止目录遍历漏洞:<?php namespace App\Controllers; use CodeIgniter\Controller; class SharedAssets extends Controller { public function serve(string $type = '', string $filename = '') { // 资源类型白名单,不在白名单内的请求直接返回404 $allowedTypes = ['css', 'js', 'img']; if (!in_array($type, $allowedTypes)) { throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); } // 拼接资源实际路径,用basename过滤非法路径字符,防止目录遍历 $filePath = ROOTPATH . 'shared_assets/' . $type . '/' . basename($filename); if (!is_file($filePath)) { throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); } // 此处可加权限校验逻辑,比如判断租户状态、用户登录状态等 // 按资源类型返回对应响应头 $mimeMap = [ 'css' => 'text/css', 'js' => 'application/javascript', 'img' => mime_content_type($filePath) ]; return $this->response ->setHeader('Content-Type', $mimeMap[$type]) ->setHeader('Cache-Control', 'public, max-age=2592000') ->setBody(file_get_contents($filePath)); } } - 第三步:添加路由规则
在app/Config/Routes.php中加入路由,适配所有租户路径下的资源请求:$routes->get('(:any)/shared-assets/(:any)/(:any)', 'SharedAssets::serve/$2/$3'); $routes->get('shared-assets/(:any)/(:any)', 'SharedAssets::serve/$1/$2'); - 第四步:视图中引用资源
用CI4自带的base_url辅助函数生成路径即可:<link rel="stylesheet" href="<?= base_url('shared-assets/css/common.css') ?>">
注意事项
- 无特殊权限需求时优先选方案1,不要用PHP处理静态资源,避免不必要的性能开销。
- 两种方案都只需要存储一份公共资源,更新时所有租户实时生效,不需要逐个租户目录复制文件。
- 所有对外提供文件访问的逻辑必须加白名单和路径过滤,避免被恶意利用读取敏感文件。
内容的提问来源于stack exchange,提问作者PRADEEP AGGARWAL
相关产品推荐
相关产品推荐

