CORS 跨域问题

5,718 阅读2分钟

背景

最近和hbb在写一个项目,采用前后端分离的模式。我写接口,hbb写前端。接口采用json的格式进行数据交互,前端和后端放在了不同的服务器上,一开始调试的时候就遇上了问题。

问题

前端POST数据到接口时,出现问题。
Google Chrome的提示:
XMLHttpRequest cannot load http://weafung.com/index.php/. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access.
Mozilla Firefox给出了更直观的提示:
已拦截跨源请求:同源策略禁止读取位于 http://weafung.com/index.php/ 的远程资源。(原因:CORS 头缺少 'Access-Control-Allow-Origin')。

解决

知道哪里出现问题后就要解决,首先了解下CORS。

什么是CORS?

来自维基百科的解释: 跨来源资源共享 - 维基百科,自由的百科全书

跨来源资源共享(CORS)是一份浏览器技术的规范,提供了 Web 服务从不同网域传来沙盒脚本的方法,以避开浏览器的同源策略,是 JSONP 模式的现代版。与 JSONP 不同,CORS 除了 GET 要求方法以外也支持其他的 HTTP 要求。用 CORS 可以让网页设计师用一般的 XMLHttpRequest,这种方式的错误处理比 JSONP 要来的好。另一方面,JSONP 可以在不支持 CORS 的老旧浏览器上运作。现代的浏览器都支持 CORS。

解决过程

在大概了解了CORS之后,初步搜索到大部分的解决方案是在PHP中添加header头:

header("Access-Control-Allow-Origin: *");

添加、测试,现在报错变了:
Google Chrome:
XMLHttpRequest cannot load http://weafung.com/index.php/. Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.
Mozilla Firefox:
已拦截跨源请求:同源策略禁止读取位于 http://weafung.com/index.php/ 的远程资源。(原因:来自 CORS 预检通道的 CORS 头 'Access-Control-Allow-Headers' 的令牌 'content-type' 无效)。

那我再加条header试试?

    header("Access-Control-Allow-Origin: *");
    header("Access-Control-Allow-Headers: *");

继续失败,还是一样的错误。

在参考 Mozilla Documentation 后发现:Access-Control-Allow-Origin是支持*,而在Access-Control-Allow-Headers部分却没有提到支持*通配符。

再来:

    header("Access-Control-Allow-Origin: *");
    header("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept");

咦,好像成功了!!!

解决方法

  • 方法一:在PHP中添加两条header语句

    header("Access-Control-Allow-Origin: *");
    header("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept");
    
  • 方法二:使用来自stackoverflow的cors方法

    function cors() {
        // Allow from any origin
        if (isset($_SERVER['HTTP_ORIGIN'])) {
            // Decide if the origin in $_SERVER['HTTP_ORIGIN'] is one
            // you want to allow, and if so:
            header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");
            header('Access-Control-Allow-Credentials: true');
            header('Access-Control-Max-Age: 86400');    // cache for 1 day
        }
        // Access-Control headers are received during OPTIONS requests
        if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
            if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']))
                header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
            if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']))
                header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}");
        }
    }
    
  • 方法三:不需要修改PHP的解决办法,使用代理或反向代理
    具体请参考:CORS 跨域问题 - Chs97's Blog

Weafung原创