PHP项目ThinkPHP跨域资源共享设置

wen PHP项目 3

本文目录导读:

PHP项目ThinkPHP跨域资源共享设置

  1. 文章标题:PHP项目ThinkPHP框架下跨域资源共享(CORS)的终极配置指南:从原理到实战
  2. 目录导读

PHP项目ThinkPHP框架下跨域资源共享(CORS)的终极配置指南:从原理到实战


目录导读

  1. 为什么你的ThinkPHP接口总被浏览器“拦截”?——CORS基础与痛点
  2. ThinkPHP 5/6/8跨域配置的三种主流方案(含代码)
  3. “终极”解决方案:中间件全局处理与预检请求(OPTIONS)
  4. 实战问答:解决携带Cookie、自定义Header、多域名白名单的疑难杂症
  5. 安全性与性能优化:如何避免“跨域”变成“跨站攻击”?
  6. 一套可复用的ThinkPHP跨域配置模板

为什么你的ThinkPHP接口总被浏览器“拦截”?——CORS基础与痛点

在前后端分离的开发模式下,前端运行在 http://localhost:8080,后端接口部署在 http://api.yourdomain.com,当浏览器发起AJAX请求时,同源策略(Same-Origin Policy)会阻止前端读取响应,这并非服务器拒绝了你,而是浏览器主动“拦截”了数据。

核心痛点: 很多PHP开发者直接在控制器里写 header('Access-Control-Allow-Origin: *');,却发现PUT、DELETE请求失败,或者无法携带Session,这是因为CORS分为“简单请求”和“预检请求(Preflight)”,对于PUT、DELETE或自定义Header,浏览器会先发送一个 OPTIONS 请求,如果你没有正确处理这个预检,真正的请求永远不会发出。

ThinkPHP 5/6/8跨域配置的三种主流方案(含代码)

控制器基类中直接添加(最易理解,适合教学)BaseControllerinitialize() 方法中加入:

protected function initialize()
{
    header('Access-Control-Allow-Origin: *');
    header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
    header('Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With');
    // 如果是预检请求,直接结束输出
    if ($this->request->isOptions()) {
        exit();
    }
    parent::initialize();
}

注意: exit() 至关重要,否则后续路由逻辑会执行。

ThinkPHP 6/8的路由中间件(官方推荐) 创建 app/middleware/CrossDomain.php

class CrossDomain
{
    public function handle($request, \Closure $next)
    {
        $allowedOrigin = ['https://www.yourdomain.com', 'http://localhost:8080'];
        $origin = $request->header('origin');
        if (in_array($origin, $allowedOrigin)) {
            header('Access-Control-Allow-Origin: ' . $origin);
            header('Access-Control-Allow-Credentials: true');
        }
        header('Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS');
        header('Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With, X-Token');
        header('Access-Control-Max-Age: 86400'); // 缓存预检结果1天
        if ($request->isOptions()) {
            return response('')->code(204);
        }
        return $next($request);
    }
}

然后在 middleware.php 注册全局中间件即可。

nginx/Apache层封装(性能最强) 在Nginx配置中添加:

location /api/ {
    add_header Access-Control-Allow-Origin $http_origin;
    add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS, PUT, DELETE';
    add_header Access-Control-Allow-Headers 'Content-Type, Authorization';
    if ($request_method = 'OPTIONS') {
        return 204;
    }
}

“终极”解决方案:中间件全局处理与预检请求(OPTIONS)

上面方案二其实已经是终极解了,但我必须补充一个关键细节:很多同学反映 Access-Control-Allow-Credentials: true 与 不能同时使用。*如果你需要携带Cookie(Session),则 Origin 必须显式指定为具体域名,不能为 ``**。

完整逻辑如下:

  1. $_SERVER['HTTP_ORIGIN'] 获取来源。
  2. 检查该来源是否在允许的白名单数组内(如:['https://a.com', 'https://b.com'])。
  3. 如果命中,动态返回该来源,并设置 Credentials: true
  4. 如果未命中,可以不返回CORS头,浏览器自然会拦截。

实战问答:解决疑难杂症

*Q1: 为什么我设置了 Allow-Origin,但请求还是失败? 因为你的请求可能携带了 withCredentials = true(即携带Cookie),浏览器要求 Origin 必须是具体地址,且 Access-Control-Allow-Credentials 必须为 true解决办法:* 将 `改为$_SERVER['HTTP_ORIGIN'],并加header('Access-Control-Allow-Credentials: true');`。

Q2: 自定义Header X-Token 总是导致预检失败? 你必须在 Access-Control-Allow-Headers明确列出这个Header名,例如前端设置了 X-Token,后端必须包含:header('Access-Control-Allow-Headers: X-Token, Content-Type');

Q3: 多域名怎么办? 不能使用 ,也不能写死,必须动态获取 Origin 并判断是否在配置数组中,如果不在白名单,直接不输出CORS头即可。

Q4: 为什么OPTIONS请求返回404? ThinkPHP 6中如果开启了路由强制匹配,OPTIONS 请求可能匹配不到路由。解决:route/app.php 中定义一条兜底路由,或者在中间件的最前端拦截并响应204。

安全性与性能优化:如何避免“跨域”变成“跨站攻击”?

CORS不是万能钥匙,如果你对 Origin 不做任何限制,直接 Allow-Origin: *,相当于任何网站都能请求你的API,容易引发CSRF攻击。

安全策略建议:

  1. 严格白名单:只允许已知的业务域名。
  2. 禁止暴露敏感HeaderAccess-Control-Expose-Headers 不要全开放。
  3. 结合CSRF Token:对于写操作(POST/PUT/DELETE),建议在Header中携带动态Token。
  4. 缓存优化:通过 Access-Control-Max-Age 减少OPTIONS请求次数,降低服务器压力。

一套可复用的ThinkPHP跨域配置模板

// 中间件核心代码
public function handle($request, \Closure $next)
{
    $allow = ['https://www.yourdomain.com'];
    $origin = $request->header('origin');
    if ($origin && in_array($origin, $allow)) {
        header('Access-Control-Allow-Origin: ' . $origin);
        header('Access-Control-Allow-Credentials: true');
        header('Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE');
        header('Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With, X-CSRF-Token');
        header('Access-Control-Max-Age: 86400');
        if ($request->isOptions()) {
            return response('', 204)->header(['Access-Control-Allow-Credentials' => 'true']);
        }
    }
    return $next($request);
}

记住三点核心:

  1. 预检请求必须死磕,OPTIONS必须在中间件或入口处被处理并立即返回204。
  2. 白名单越严格越好,能指定域名绝不使用通配符。
  3. 调试技巧:使用Chrome DevTools查看 Network 面板中的 Response Headers,如果看到 Access-Control-Allow-Origin 存在且符合预期,浏览器就不会拦你。

通过以上配置,你的ThinkPHP项目不仅能完美支持Vue、React等现代前端框架,还能通过谷歌和必应的“移动端可用性”与“安全浏览”SEO评审。注意别在测试环境用Chrome禁用安全策略来摸鱼,正确配置才能应对高并发生产环境。

抱歉,评论功能暂时关闭!