TP6 如何配置跨域 CORS 中间件允许特定域名访问

文章导读
ThinkPHP 6 配置跨域 CORS 中间件允许特定域名访问,最推荐的做法是自定义中间件动态匹配 Origin 白名单,而不是简单设为*,尤其当前端需要携带 Cookie 或 Authorization 头时。
📋 目录
  1. 第一步:创建配置文件
  2. 第二步:编写自定义中间件
  3. 第三步:注册中间件
  4. 多环境配置切换
  5. 验证方法
  6. 常见坑
A A

ThinkPHP 6 配置跨域 CORS 中间件允许特定域名访问,最推荐的做法是自定义中间件动态匹配 Origin 白名单,而不是简单设为*,尤其当前端需要携带 Cookie 或 Authorization 头时。

先说结论:TP6 跨域必须通过中间件统一处理,控制器里不要手动设置 CORS 头,带凭证请求时 Origin 不能为通配符。

  • 适合:前后端分离项目、多域名访问场景、需要携带凭证的 API 接口
  • 先看:中间件注册位置是否在路由之前、是否有重复设置响应头
  • 建议:生产环境用白名单校验 Origin,开发环境可临时用*测试

第一步:创建配置文件

config目录下新建cors.php,将域名白名单集中管理,方便不同环境切换。支持读取环境变量,避免代码硬编码。

<?php
return [
    // 允许的域名列表,支持数组或单个字符串
    // 生产环境建议通过 .env 配置 CORS_ALLOW_ORIGIN 变量
    'allow_origin' => explode(',', env('CORS_ALLOW_ORIGIN', 'http://localhost:3000,https://admin.example.com')),
    
    // 允许的 HTTP 方法
    'allow_methods' => ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
    
    // 允许的请求头
    'allow_headers' => ['Content-Type', 'Authorization', 'X-Requested-With'],
    
    // 是否允许携带凭证 (Cookie)
    'allow_credentials' => true,
];

第二步:编写自定义中间件

app/middleware/目录下新建CorsMiddleware.php。中间件需读取上述配置,动态校验 Origin 并写入响应头。注意处理 OPTIONS 预检请求直接返回 204 状态码。

TP6 如何配置跨域 CORS 中间件允许特定域名访问
namespace app\middleware;
use think\Response;

class CorsMiddleware
{
    public function handle($request, \Closure $next)
    {
        $origin = $request->header('Origin');
        $allowOrigin = config('cors.allow_origin', []);
        $response = $next($request);
        
        // 校验 Origin 是否在白名单
        if ($origin && (in_array($origin, $allowOrigin) || in_array('*', $allowOrigin))) {
            // 如果白名单包含 * 且允许凭证,浏览器会报错,需确保生产环境不使用 * 搭配 credentials
            $response->header('Access-Control-Allow-Origin', $origin);
            if (config('cors.allow_credentials', false)) {
                $response->header('Access-Control-Allow-Credentials', 'true');
            }
        }
        
        $response->header('Access-Control-Allow-Methods', implode(',', config('cors.allow_methods', [])));
        $response->header('Access-Control-Allow-Headers', implode(',', config('cors.allow_headers', [])));
        
        // 处理预检请求,直接返回 204 避免执行后续逻辑
        if ($request->isOptions()) {
            return response('', 204);
        }
        
        return $response;
    }
}

第三步:注册中间件

编辑app/middleware.php,将中间件添加到全局数组靠前位置,确保在路由解析前执行。

return [
    // 全局中间件
    \app\middleware\CorsMiddleware::class,
    // 其他中间件...
];

多环境配置切换

开发环境和生产环境的域名不同,建议通过根目录.env文件区分。在.env中设置:

TP6 如何配置跨域 CORS 中间件允许特定域名访问
# 开发环境
CORS_ALLOW_ORIGIN=http://localhost:3000,http://127.0.0.1:8080

# 生产环境部署时替换为
CORS_ALLOW_ORIGIN=https://admin.example.com,https://www.example.com

部署时只需替换.env文件或配置服务器环境变量,无需修改代码。若使用 CI/CD 流程,可在构建阶段生成不同的config/cors.php文件。

验证方法

使用浏览器开发者工具或 curl 检查响应头。打开控制台 Network 面板,发起一个跨域请求,查看响应头里是否有Access-Control-Allow-Origin且值是你配置的域名。也可以用命令:

curl -I -H "Origin: http://localhost:3000" http://your-api.com/api/test

如果返回头里包含Access-Control-Allow-Origin: http://localhost:3000说明配置生效。注意 OPTIONS 预检请求也要返回 CORS 头,否则真实请求发不出去。

常见坑

  • 重复设置头:控制器里不要再用header()response()->header()设置 CORS 相关头,会和中间件冲突导致浏览器报错。
  • 中间件顺序:中间件必须放在app/middleware.php全局数组的靠前位置,确保最先执行。
  • 凭证与通配符:如果前端请求带了credentials: 'include',后端 Origin 不能为*,必须精确匹配,否则浏览器会拦截。
  • Nginx 代理:Nginx 反向代理可能会过滤或覆盖响应头,需要确认代理配置透传 Origin,检查proxy_pass相关配置。
  • 内置中间件限制:内置的\think\middleware\Cors::class不支持数组形式的多域名白名单,需要自定义中间件实现动态匹配。