- 后端
- Web框架
【免费下载链接】CodeIgniter4
Open Source PHP Framework (originally from EllisLab)
CORS(Cross-Origin Resource Sharing,跨域资源共享)是基于 HTTP 头的安全机制,它允许服务器声明:除自身之外的哪些源(域名、协议或端口)可以被浏览器授权加载资源。CodeIgniter 4 自 4.5.0 起内置了 CORS 过滤器与辅助类(cors过滤器 +CodeIgniter\HTTP\Cors类),本文将以官方文档 cors.rst 为骨架,结合仓库源码与测试,完整讲解配置项、路由/过滤器两种启用方式、预检(Preflight)请求处理、多配置切换以及底层实现原理。读完本文,你将能为 API 或前后端分离应用正确配置、验证并调试 CORS,同时避免通配符与凭据共用、缓存Vary头缺失等常见坑点。
CORS 与 CodeIgniter 4 的解决方案
跨域资源共享机制通过在 HTTP 请求与响应中添加头部,向浏览器表明目标资源是否允许跨源共享,从而帮助防御跨站请求伪造(CSRF)与数据窃取等恶意攻击。如果你对 CORS 请求头、预检请求等基础概念尚不熟悉,建议先阅读 MDN 关于 CORS 的文档(可在 app/Config/Cors.php 头部注释中找到官方推荐链接)。
CodeIgniter 4 为开发者提供了两个层次的 CORS 支持(4.5.0 版本新增,见 v4.5.0 更新日志):
- CORS 过滤器:实现位于 system/Filters/Cors.php,通过
before/after生命周期自动注入响应头、终结预检请求; - CORS 辅助类:实现位于 system/HTTP/Cors.php,负责实际的头生成、源校验与配置工厂,过滤器内部即委托该类工作。
过滤器别名cors已在 system/Config/Filters.php 与 app/Config/Filters.php 的$aliases中预注册,指向CodeIgniter\Filters\Cors,开箱即用。
配置 CORS:$default配置项详解
CORS 的默认配置统一放在app/Config/Cors.php的$default属性中。完整默认配置如下(仓库实际文件 app/Config/Cors.php):
<?php namespace Config; use CodeIgniter\Config\BaseConfig; class Cors extends BaseConfig { public array $default = [ 'allowedOrigins' => [], 'allowedOriginsPatterns' => [], 'supportsCredentials' => false, 'allowedHeaders' => [], 'exposedHeaders' => [], 'allowedMethods' => [], 'maxAge' => 7200, ]; }必须设置的三个核心项
文档明确指出,$default中以下三项是开启 CORS 的最低要求:
| 配置项 | 含义 | 对应响应头 |
|---|---|---|
allowedOrigins | 显式列出允许的 Origin(如['http://localhost:8080']、['https://www.example.com']) | Access-Control-Allow-Origin |
allowedHeaders | 显式列出允许的 HTTP 请求头(如['Authorization', 'Content-Type']) | Access-Control-Allow-Headers |
allowedMethods | 显式列出允许的 HTTP 方法(如['GET', 'POST', 'PUT', 'DELETE']) | Access-Control-Allow-Methods |
⚠️最小权限原则:基于 least privilege 原则,只应允许最低限度的 Origin、Methods 与 Headers,切勿贪多。
其余配置项
allowedOriginsPatterns:Origin 正则模式列表。文档与源码注释说明,每个模式会被包成#\A<pattern>\z#进行整串匹配,例如['https://\w+\.example\.com']可匹配https://api.example.com。匹配成功的源会动态回填到Access-Control-Allow-Origin,并追加Vary: Origin;supportsCredentials:若跨源请求携带凭据(如 Cookie),必须设为true,此时会输出Access-Control-Allow-Credentials: true;exposedHeaders:允许浏览器脚本读取的响应头列表(Access-Control-Expose-Headers),如['Content-Length', 'X-Kuma-Revision'];maxAge:预检请求结果可被浏览器缓存的最大秒数(Access-Control-Max-Age),默认7200(2 小时)。
源码中的合法性约束
在 system/HTTP/Cors.php 中有两条硬性校验,配置不合规会抛出ConfigException:
- 通配符只能单独使用:
checkWildcard()要求若配置中使用了'*',则该数组必须恰好为['*'](元素个数为 1),否则抛错; - 凭据请求禁止通配符:
checkWildcardAndCredentials()规定当supportsCredentials为true时,Access-Control-Allow-Origin与Access-Control-Allow-Headers的值不得是"*"通配符(浏览器规范要求),源码注释也明确提示"不推荐使用通配符"。
启用 CORS:过滤器 + OPTIONS 路由缺一不可
启用 CORS 需要同时完成两件事:
- 为允许 CORS 的路由指定
cors过滤器; - 为 CORS 预检请求添加OPTIONS路由。
⚠️关键警告:除了 Required Filters(必需过滤器,见 app/Config/Filters.php 的
$required列表)之外,控制器过滤器在路由不存在时不会执行。因此不添加 OPTIONS 路由,预检请求将直接 404,CORS 功能失效。而 CORS 过滤器会接管所有预检请求,所以 OPTIONS 路由的闭包控制器通常不会被真正调用。
方式一:在 Routes.php 中按路由组设置
在app/Config/Routes.php中为路由组绑定cors过滤器(示例源自 cors/001.php):
use CodeIgniter\Router\RouteCollection; $routes->group('', ['filter' => 'cors'], static function (RouteCollection $routes): void { $routes->resource('product'); // 为预检请求添加 OPTIONS 路由 $routes->options('product', static function () { // 如需处理普通的非预检 OPTIONS 请求,可在此实现逻辑 $response = response(); $response->setStatusCode(204); $response->setHeader('Allow:', 'OPTIONS, GET, POST, PUT, PATCH, DELETE'); return $response; }); $routes->options('product/(:any)', static function () {}); });这里$routes->options('product/(:any)', ...)覆盖了资源路由的所有子路径预检;$routes->options('product', ...)中演示了如何处理非预检的普通 OPTIONS 请求(返回204并附带Allow头)。
方式二:在 Config/Filters.php 中按 URI 路径设置
也可以改用app/Config/Filters.php的$filters属性按 URI 模式匹配(示例源自 cors/002.php):
namespace Config; use CodeIgniter\Config\Filters as BaseFilters; class Filters extends BaseFilters { public array $filters = [ 'cors' => [ 'before' => ['api/*'], 'after' => ['api/*'], ], ]; }同样必须补充 OPTIONS 路由(示例源自 cors/003.php):
use CodeIgniter\Router\RouteCollection; $routes->group('', ['filter' => 'cors'], static function (RouteCollection $routes): void { $routes->options('api/(:any)', static function () {}); });注意此方式将过滤器配置在路由之外的$filters属性中,所有匹配api/*的请求(无论 GET、POST 还是 OPTIONS)都会经过cors过滤器。
多配置:cors:api与过滤器参数
当不同路由组需要不同策略时,可为app/Config/Cors.php添加新的属性作为独立配置。属性名即配置名,例如新增$api(示例源自 cors/004.php):
namespace Config; use CodeIgniter\Config\BaseConfig; class Cors extends BaseConfig { // ... $default ... public array $api = [ 'allowedOrigins' => ['https://app.example.com'], 'allowedOriginsPatterns' => [], 'supportsCredentials' => true, 'allowedHeaders' => ['Authorization', 'Content-Type'], 'exposedHeaders' => [], 'allowedMethods' => ['GET', 'POST', 'PUT', 'DELETE'], 'maxAge' => 7200, ]; }然后在路由过滤器参数中以cors:api的形式指定该配置名(示例源自 cors/005.php):
use CodeIgniter\Router\RouteCollection; $routes->group('api', ['filter' => 'cors:api'], static function (RouteCollection $routes): void { $routes->resource('user'); $routes->options('user', static function () {}); $routes->options('user/(:any)', static function () {}); });其底层机制是:过滤器before()收到参数数组$arguments后,调用CorsService::factory($arguments[0])按名字取出对应配置(见 system/Filters/Cors.php 与 system/HTTP/Cors.php 的factory())。同样的参数语法也可用于$filters属性,例如'cors:api' => ['before' => ['api/*'], 'after' => ['api/*']],即文档中提到的 过滤器参数 功能(自 4.4.0 起支持,4.6.0 起spark输出表中会显示过滤器参数)。
验证配置:spark routes 与 spark filter:check
配置完成后,可用spark命令核对路由与过滤器:
php spark routes该命令会列出全部路由及其绑定的过滤器(含闭包路由、自动路由与过滤器信息,详见 路由文档)。由于路由正则表达式可能导致过滤器显示不准,文档还推荐使用更精确的过滤器检查命令(详见 过滤器文档):
php spark filter:check get /输出会以表格形式呈现Method | Route | Before Filters | After Filters,并列出实际执行的过滤器类名(自 4.6.0 起同时显示过滤器参数)。
源码级原理:CodeIgniter\HTTP\Cors 的工作方式
CORS 辅助类的完整实现在 system/HTTP/Cors.php,以下三个公开方法对应文档"类参考"部分:
isPreflightRequest(IncomingRequest $request): bool
判断请求是否为预检请求,判定条件只有两个(源码 L80-L84):
return $request->is('OPTIONS') && $request->hasHeader('Access-Control-Request-Method');即:请求方法为OPTIONS且携带Access-Control-Request-Method头。这正好被 tests/system/HTTP/CorsTest.php 中的testIsPreflightRequestTrue/False用例覆盖:仅有OPTIONS方法但无该头时返回false。
handlePreflightRequest(RequestInterface $request, ResponseInterface $response): ResponseInterface
处理预检请求(源码 L89-L103)。流程为:
- 设置响应状态码
204 No Content; - 调用
setAllowOrigin()校验请求 Origin 并输出Access-Control-Allow-Origin; - 仅当Origin 校验通过(响应中已有该头)时,才依次输出
Access-Control-Allow-Headers、Access-Control-Allow-Methods、Access-Control-Max-Age与(如启用)Access-Control-Allow-Credentials。
Origin 匹配有三个分支(源码 L129-L170),与测试用例一一对应:
- 单一 Origin:
allowedOrigins恰好 1 个且无正则时,直接回显该值(测试testHandlePreflightRequestSingleAllowedOrigin); - 多个 Origin:从请求的
Origin头精确匹配,命中则回显该 Origin 并追加Vary: Origin;未命中则不输出任何 CORS 头(测试testHandlePreflightRequestMultipleAllowedOriginsAllowed/NotAllowed); - 正则模式:依次用
#\A<pattern>\z#匹配,命中则回显并追加Vary: Origin(测试testHandlePreflightRequestAllowedOriginsPatternsAllowed/NotAllowed)。
Vary: Origin的存在意义在于:响应内容随 Origin 变化,必须告知 CDN/代理缓存按 Origin 区分缓存条目,防止跨源缓存污染。
addResponseHeaders(RequestInterface $request, ResponseInterface $response): ResponseInterface
为普通(非预检)跨源请求添加响应头(源码 L209-L219):设置Access-Control-Allow-Origin后,若校验通过再补充Access-Control-Allow-Credentials与Access-Control-Expose-Headers。注意普通请求不会输出Allow-Headers/Allow-Methods(测试testAddResponseHeadersSingleAllowedOriginSimpleRequest已断言这两个头不存在)。
过滤器如何衔接:before / after 与 Vary 头
system/Filters/Cors.php 的before()中,若判定为预检请求则直接返回handlePreflightRequest()的结果并短路后续控制器执行;同时无论预检还是普通 OPTIONS 请求,都会追加Vary: Access-Control-Request-Method头(源码注释解释:若有 CDN 等中间缓存,普通 OPTIONS 与有效预检请求会被分开缓存,避免错误命中)。after()则通过hasResponseHeaders()判断响应是否已被(例如其他过滤器)注入过 CORS 头,避免重复添加——这正是 v4.6.1 更新日志 中"修复其他过滤器在 before 阶段返回响应对象时 CORS 头未添加"问题的设计所在。
类参考速查
以下为 system/HTTP/Cors.php 对外公开的 API 摘要:
| 方法 | 签名 | 说明 |
|---|---|---|
addResponseHeaders | addResponseHeaders(RequestInterface $request, ResponseInterface $response): ResponseInterface | 为跨源请求添加 CORS 响应头 |
handlePreflightRequest | handlePreflightRequest(RequestInterface $request, ResponseInterface $response): ResponseInterface | 处理预检请求(返回204及全套 CORS 头) |
isPreflightRequest | isPreflightRequest(IncomingRequest $request): bool | 判断请求是否为预检请求(OPTIONS+Access-Control-Request-Method) |
除此之外,构造函数与factory(string $configName = 'default')支持直接以配置数组或CorsConfig实例初始化,hasResponseHeaders()供过滤器判断头部是否已设置。完整行为可对照单元测试 tests/system/HTTP/CorsTest.php 阅读,其中覆盖了单源/多源/正则匹配、凭据、暴露头、Vary头合并等全部场景。
小结
在 CodeIgniter 4 中启用 CORS 遵循一条固定链路:先在 app/Config/Cors.php 配置$default(或自定义命名配置),再在 app/Config/Routes.php / app/Config/Filters.php 绑定cors(或cors:配置名)过滤器,最后务必为路由补充 OPTIONS 预检路由。配置完成后用php spark routes与php spark filter:check验证,配合本文给出的源码分支与测试用例,即可快速定位 Origin 未匹配、凭据与通配符冲突、缓存Vary头缺失等绝大多数跨域问题。
- 后端
- Web框架
【免费下载链接】CodeIgniter4
Open Source PHP Framework (originally from EllisLab)
相关推荐
如何快速入门EasyRec:5分钟搭建你的第一个推荐模型
如何快速入门EasyRec:5分钟搭建你的第一个推荐模型 EasyRec是一个功能强大的大规模推荐算法框架,专为快速构建高效推荐系统而设计。无论你是推荐系统新手
后端机器学习深度学习Automa跨域资源共享:CORS配置与预检请求处理
Automa跨域资源共享:CORS配置与预检请求处理 概述 在Web开发中,跨域资源共享(CORS,Cross Origin Resource Sharing)
RPA工作流自动化浏览器控制网页爬虫WinterJS 跨域资源共享:CORS配置与预检请求处理
WinterJS 跨域资源共享:CORS配置与预检请求处理 什么是跨域资源共享(CORS) 在现代Web开发中,浏览器出于安全考虑实施了同源策略(Same Or
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考