首页 > web前端 > js教程 > 正文

解决跨域资源共享(CORS)问题:PHP与JavaScript实战教程

心靈之曲
发布: 2025-11-17 17:12:05
原创
777人浏览过

解决跨域资源共享(cors)问题:php与javascript实战教程

本教程深入探讨了Web开发中常见的跨域资源共享(CORS)问题,特别是当JavaScript通过Fetch API请求PHP后端时遇到的挑战。文章详细解释了CORS机制、预检请求(OPTIONS)的处理,并提供了PHP服务器端配置CORS响应头的完整示例代码,指导开发者如何正确配置Access-Control-Allow-Origin、Access-Control-Allow-Methods等,以确保跨域请求成功。

1. 理解跨域资源共享(CORS)

跨域资源共享(Cross-Origin Resource Sharing, CORS)是一种浏览器安全机制,旨在允许网页从不同域(协议、域名或端口)请求资源。由于浏览器的同源策略(Same-Origin Policy)限制,默认情况下,JavaScript只能与同源的服务器进行交互。当前端应用(例如运行在 http://localhost:3000)尝试向不同源的后端API(例如 https://api.example.com)发送请求时,浏览器会触发CORS机制,并要求服务器明确授权该跨域请求。如果服务器未正确配置CORS响应头,浏览器将阻止请求,并抛出“Cross-Origin Request Blocked”错误。

2. CORS请求的类型与预检请求

CORS请求主要分为两种类型:

  • 简单请求 (Simple Requests):满足特定条件的GET、HEAD或POST请求。这些请求不会触发CORS预检。条件包括:
    • HTTP方法是GET、HEAD或POST。
    • Content-Type 仅限于 application/x-www-form-urlencoded、multipart/form-data 或 text/plain。
    • 不使用自定义请求头。
  • 非简单请求 (Preflighted Requests):不满足简单请求条件的请求,例如使用了PUT、DELETE方法,或POST请求的Content-Type为application/json,或使用了自定义请求头。对于非简单请求,浏览器会在发送实际请求之前,自动发送一个HTTP OPTIONS方法请求到服务器,这就是所谓的“预检请求”(Preflight Request)。预检请求的目的是询问服务器是否允许实际的跨域请求。服务器必须响应OPTIONS请求,并包含适当的CORS响应头,告知浏览器允许哪些源、方法和头。如果预检成功,浏览器才会发送实际请求;否则,请求会被阻止。

3. 关键的CORS响应头

为了正确处理CORS请求,服务器需要在响应中包含一系列Access-Control-*头:

立即学习PHP免费学习笔记(深入)”;

AI建筑知识问答
AI建筑知识问答

用人工智能ChatGPT帮你解答所有建筑问题

AI建筑知识问答 22
查看详情 AI建筑知识问答
  • Access-Control-Allow-Origin: 必需。指定允许访问资源的来源。可以是一个具体的URL(例如 https://your-frontend-domain.com),也可以是通配符 *(表示允许任何来源,但在生产环境中应谨慎使用,因为它可能带来安全风险)。
  • Access-Control-Allow-Methods: 必需。指定允许跨域访问的HTTP方法,例如 GET, POST, OPTIONS, PUT, DELETE。
  • Access-Control-Allow-Headers: 必需。指定允许跨域请求携带的自定义请求头。如果请求中包含 Content-Type: application/json 或其他自定义头,服务器必须在此处列出它们。
  • Access-Control-Allow-Credentials: 可选。如果前端需要发送Cookie或HTTP认证凭证(例如Authorization头),此头必须设置为 true。注意,当此头为 true 时,Access-Control-Allow-Origin 不能设置为 *,必须指定具体的来源。
  • Access-Control-Max-Age: 可选。指定预检请求的结果可以被浏览器缓存多长时间(秒)。在此期间,浏览器无需再次发送OPTIONS预检请求。

4. PHP后端CORS配置实战

以下是一个PHP后端正确配置CORS以处理JavaScript跨域请求的示例。该示例着重于处理OPTIONS预检请求,并动态设置Access-Control-Allow-Origin。

<?php
class ControllerCustomapiTest extends Controller {
    public function index() {
        // 1. 设置Access-Control-Allow-Origin
        // 优先使用HTTP_ORIGIN头来确定请求来源,并动态设置允许的来源
        // 建议在这里维护一个允许的来源白名单,以增强安全性
        if (isset($_SERVER['HTTP_ORIGIN'])) {
            $origin = $_SERVER['HTTP_ORIGIN'];
            // 示例:可以根据白名单判断是否允许该来源
            // $allowedOrigins = ['http://localhost:4200', 'https://your-frontend-domain.com'];
            // if (in_array($origin, $allowedOrigins)) {
            //     header("Access-Control-Allow-Origin: " . $origin);
            // } else {
            //     // 如果来源不在白名单中,可以拒绝请求
            //     http_response_code(403);
            //     die('Forbidden Origin');
            // }
            header("Access-Control-Allow-Origin: " . $origin); // 简化示例:允许任何发送HTTP_ORIGIN的来源
        } else {
            // 如果没有HTTP_ORIGIN头(通常是同源请求或某些非浏览器请求),
            // 可以设置一个默认的允许来源,或者根据实际情况设置为 '*' (谨慎使用)
            header("Access-Control-Allow-Origin: *"); // 生产环境请务必替换为具体域名
        }

        // 2. 设置Access-Control-Allow-Credentials (如果需要发送Cookie或认证头)
        header('Access-Control-Allow-Credentials: true');

        // 3. 设置Access-Control-Max-Age (缓存预检请求结果,单位:秒)
        header('Access-Control-Max-Age: 86400'); // 缓存1天

        // 4. 处理预检 OPTIONS 请求
        if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
            // 响应允许的HTTP方法
            if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'])) {
                header("Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS");
            }
            // 响应允许的请求头
            if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])) {
                header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}");
            }
            // 预检请求成功,返回204 No Content状态码并终止脚本
            http_response_code(204);
            die();
        }

        // 5. 处理实际请求 (GET, POST, PUT, DELETE 等)
        // 设置响应内容类型为JSON
        header('Content-Type: application/json');

        // 构建并发送JSON响应
        $response = array('success' => true, 'message' => '数据已成功接收');
        echo json_encode($response);
    }
}
?>
登录后复制

代码解释:

  • Access-Control-Allow-Origin: 优先检查 $_SERVER['HTTP_ORIGIN'] 来获取请求来源。在生产环境中,强烈建议您根据一个预定义的白名单来验证 HTTP_ORIGIN,而不是无条件地回显它或使用 *。
  • Access-Control-Allow-Credentials: 如果前端请求需要携带认证信息(如Cookie或Authorization头),必须将其设置为 true。同时,Access-Control-Allow-Origin 不能为 *。
  • Access-Control-Max-Age: 缓存预检请求的有效时间。在此时间内,浏览器将不再发送重复的OPTIONS请求。
  • if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS'): 这是处理预检请求的关键。
    • 它会根据浏览器在 HTTP_ACCESS_CONTROL_REQUEST_METHOD 和 HTTP_ACCESS_CONTROL_REQUEST_HEADERS 中声明的需求,设置相应的 Access-Control-Allow-Methods 和 Access-Control-Allow-Headers。
    • 预检请求成功后,服务器应返回 204 No Content 状态码,并且必须使用 die() 终止脚本执行,以防止发送实际请求的响应内容。
  • 实际请求处理: 在 OPTIONS 块之后,才是处理 GET, POST 等实际请求的逻辑。此时,CORS头已经设置完毕,可以正常返回业务数据。

5. JavaScript客户端请求示例

前端JavaScript代码通常不需要为CORS做特殊处理,只需按照常规方式发送请求即可。浏览器会自动处理预检请求和CORS验证。

const url = 'https://opecart9349.000webhostapp.com/unzipper/opencart/index.php?route=customapi/test'; // 替换为你的PHP后端URL

fetch(url, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json', // 触发非简单请求,需要预检
    // 'Authorization': 'Bearer your_token_here' // 如果需要发送认证头,服务器也需在Allow-Headers中允许
  },
  body: JSON.stringify({ name: 'John Doe', age: 30 }) // 示例数据
})
  .then(response => {
    // 检查HTTP状态码,处理非2xx响应
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    return response.json();
  })
  .then(data => {
    console.log('Success:', data);
  })
  .catch(error => {
    console.error('Error:', error);
  });
登录后复制

6. 注意事项与最佳实践

  • 安全性优先:永远不要在生产环境中使用 Access-Control-Allow-Origin: *,除非你明确知道所有资源都应该对所有来源开放。这可能导致跨站请求伪造(CSRF)等安全漏洞。
  • 精确指定来源:始终将 Access-Control-Allow-Origin 设置为你的前端应用的具体域名(或一个域名白名单),例如 https://your-frontend-domain.com。
  • 正确处理 OPTIONS 请求:这是解决CORS问题的最常见痛点。确保你的服务器能够识别并正确响应OPTIONS请求,返回正确的CORS头,并立即终止脚本。
  • 使用 $_SERVER['HTTP_ORIGIN']:在动态设置 Access-Control-Allow-Origin 时,优先使用 $_SERVER['HTTP_ORIGIN'] 来获取请求来源,而不是 $_SERVER['HTTP_REFERER'],因为 HTTP_ORIGIN 是专门为CORS设计的,而 HTTP_REFERER 可能不可靠或缺失。
  • 调试工具:利用浏览器开发者工具(通常是F12)的网络(Network)选项卡,检查请求和响应的HTTP头。特别关注OPTIONS请求的响应头,确保所有Access-Control-*头都正确设置。
  • 服务器配置:除了代码中的CORS头,有时Web服务器(如Apache或Nginx)的配置也可能影响CORS。确保它们没有覆盖或阻止你的PHP脚本设置的CORS头。

7. 总结

跨域资源共享(CORS)是Web开发中不可避免的一部分。正确理解CORS机制,特别是预检请求的作用,并按照本文提供的PHP后端配置示例,可以有效地解决“Cross-Origin Request Blocked”错误。核心在于服务器端要明确告知浏览器,哪些跨域请求是被允许的,以及允许哪些方法、头和来源。通过细致的配置和严谨的安全考量,可以确保您的Web应用在不同域之间安全、顺畅地通信。

以上就是解决跨域资源共享(CORS)问题:PHP与JavaScript实战教程的详细内容,更多请关注php中文网其它相关文章!

PHP速学教程(入门到精通)
PHP速学教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号