php 返回 Access-Control-Allow-Origin 请求头,实现 ajax 异步请求跨域资源共享

跨域资源共享 2020-04-11 阅读 31 评论 0

问题描述

在当前域名 http://test.com 下的页面,使用 Jquery 的 .ajax() 异步请求另一个域名 http://app.com

$.ajax({
    url: "http://app.com/index.php",
    data: {test: "test"},
    dataType: "json",
    method: "post",
    success: function() {
        console.log("success");
    },
    error: function() {
        console.log("error");
    }
});

浏览器控制台报错了。

Access to XMLHttpRequest at 'http://app.com/index.php' from origin 'http://test.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
POST http://app.com/index.php net::ERR_FAILED

问题解决

浏览器基于 同源策略(same-origin policy),会阻止不同域的异步请求。除非被请求的域名返回 Access-Control-Allow-Origin 请求头,允许跨域访问。我们先认识一下请求头 Origin。

1. 了解 Http 请求头 Origin

Http 的 Headers Origin ,指示了请求来自于哪个站点。如果一个异步 ajax 访问的 url,不是当前域名,浏览器在发送请求的时候,会带上 Origin 的请求头。Origin 的格式是:

Origin: <scheme> "://" <host> [ ":" <port> ]

以下是一个异步请求的示例,原站点的 url 是 http://test.com/test/cors.html ,请求 http://app.com/index.php ,浏览器自动加上 Origin 请求头 http://test.com,Google Chrome 浏览器的网络截图如下。

2. PHP 设置跨域 Access-Control-Allow-Origin 请求头

上面说到的 Origin 请求头的值,与 Access-Control-Allow-Origin 请求头的值其实是一致的。

2.1 匹配一个域名

返回一个域名的请求头,格式如:Access-Control-Allow-Origin: http://test.com

if (isset($_SERVER["HTTP_ORIGIN"]) && $_SERVER["HTTP_ORIGIN"] == "http://test.com") {
    header("Access-Control-Allow-Origin: " . $_SERVER["HTTP_ORIGIN"]);
}

2.2 动态匹配多个域名

http 回应头 Response  Headers,每次只能返回一个Access-Control-Allow-Origin ,即只能一个域名,可以通过 PHP 动态处理,使用 in_array 或者 preg_match 等方法。

$arr = ["http://test.com", "http://baidu.com"];
if (isset($_SERVER["HTTP_ORIGIN"]) && in_array($_SERVER["HTTP_ORIGIN"], $arr)) {
    header("Access-Control-Allow-Origin: " . $_SERVER["HTTP_ORIGIN"]);
}

2.3 匹配所有域名

返回 * 号,匹配所有域名,格式如:Access-Control-Allow-Origin: *

header("Access-Control-Allow-Origin: *");
最后更新 2020-04-11
MIP.watch('startSearch', function (newVal, oldVal) { if(newVal) { var keyword = MIP.getData('keyword'); console.log(keyword); // 替换当前历史记录,新增 MIP.viewer.open('/s/' + keyword, {replace: true}); setTimeout(function () { MIP.setData({startSearch: false}) }, 1000); } }); MIP.watch('goHome', function (newVal, oldVal) { MIP.viewer.open('/', {replace: false}); });