Home » Code » ajax跨域请求

ajax跨域请求

开发者经常遇到这么一个问题:浏览器拒绝访问远程资源。通常这个问题发生在使用jQuery或者原始XMLHttpRequest来执行一个跨域请求的时候,造成的结果就是ajax请求没有执行结果没有获取得到。

同源策略

这是一个安全策略,它定义了网页如何访问外部资源(如字体、ajax请求)的规则。根据同源策略,网络浏览器不允许网页访问与当前网页源不同的资源。当资源的协议、主机名或者端口不同时,则认为源不同。要克服同源安全策略的限制,使用跨源资源共享或者简称为CROS(Cross-Origin-Resource-Sharing)的技术是有可能实现的。

跨源资源共享

CROS是一种机制,它定义了浏览器与web服务器进行交互以确认是否允许网页从不同源来访问资源的过程。

当你要执行一个跨源请求,浏览器会将当前域名作为头部源发送。

Origin: http://localhost

当服务器接收到这个请求,会检查源是否在允许列表当中,并通过Access-Control-Allow-Origin发出响应。

Access-Control-Allow-Origin: http://localhost

如果你想允许所有,可以使用通配符’*’。

Access-Control-Allow-Origin: *

ajax跨域请求

1、简单请求

一个简单的跨域请求是下而这样的:

  • 不发送自定义头
  • 只使用GET、POST或者HEAD请求方法
// jQuery cross domain ajax
$.get("http://dev.xiaomlove.com/ajax.php").done(function (data) {
    console.log(data);
});

// using XMLHttpRequest
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://dev.xiaomlove.com/ajax.php", true);
xhr.onload = function () {
    console.log(xhr.responseText);
};
xhr.send();

2、预检请求

设置自定义头会触发预检请求(Preflighted Request)。简单来说就是预检请求会先通过OPTIONS方法向远程域名上的资源发送一个HTTP请求,以确保请求能安全发送。根据W3C规定,对于使用HTTP GET方法的不同源请求,当设置了除Accept和Accept-Language外的头部时,会进行预检请求。

// jQuery preflight request
$.ajax({
    type: "GET",
    headers: {"X-My-Custom-Header": "some value"},
    url: "http://dev.xiaomlove.com/ajax.php"
}).done(function (data) {
    console.log(data);
});

// XMLHttpRequest preflight request
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://dev.xiaomlove.com/ajax.php", true);
xhr.setRequestHeader("X-My-Custom-Header", "some value");
xhr.onload = function () {
    console.log(xhr.responseText);
};
xhr.send();

3、带证书请求

默认情况下,非同源请求,浏览器不会发送证书(比如HTTP Cookies, HTTP Authentication及client-side SSL certificates),若需要激活这个功能,需要在XMLHttpRequest对象上设置一个特定属性。

// jQuery CORS example
$.ajax({
    xhrFields: {
        withCredentials: true
    },
    type: "GET",
    url: "http://dev.xiaomlove.com/ajax.php"
}).done(function (data) {
    console.log(data);
});

// XMLHttpRequest
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://dev.xiaomlove.com/ajax.php", true);
xhr.withCredentials = true;
xhr.onload = function () {
    console.log(xhr.responseText);
};
xhr.send();

4、服务器响应

下面我们来看看服务器需要怎么响应:

// http://dev.xiaomlove.com/ajax.php
if (!isset($_SERVER['HTTP_ORIGIN'])) {
    // 这不是跨域请求
    exit;
}

$wildcard = FALSE; // 设置$wildcard为TRUE, 如果不想检查或限制域名   
$credentials = TRUE; // 设置$credentials为TURE,如果期望带证书请求 (Cookies, Authentication, SSL certificates)
$allowedOrigins = array('http://localhost', 'http://jsfiddle.net');
if (!in_array($_SERVER['HTTP_ORIGIN'], $allowedOrigins) && !$wildcard) {
    // 来源不被允许
    exit;
}
$origin = $wildcard && !$credentials ? '*' : $_SERVER['HTTP_ORIGIN'];

header("Access-Control-Allow-Origin: " . $origin);
if ($credentials) {
    header("Access-Control-Allow-Credentials: true");
}
header("Access-Control-Allow-Methods: POST, GET, OPTIONS");
header("Access-Control-Allow-Headers: Origin, X-My-Custon-Header");
header('P3P: CP="CAO PSA OUR"'); // 让IE支持cookie
// 处理预检请求
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') { 
    exit;
}

// 进行响应
header("Content-Type: application/json; charset=utf-8");
echo json_encode(array('status' => 'OK', 'cookies' => $_COOKIE, 'params' => $_REQUEST));

注意:

  • 当credentials为true时,不能在Access-Control-Allow-Origin中使用通配符*
  • Gecko 11.0 (Firefox 11.0 / Thunderbird 11.0 / SeaMonkey 2.8) 在异步请求中移除了对withCredentios属性的支持

浏览器兼容性

Chrome 3+, Firefox 3.5+, IE 10+, Opera 12+, Safari 4+

PS:对于预检请求,可能不是很熟悉,看图:

预检请求(Preflighted Request)

翻译自:https://zinoui.com/blog/cross-domain-ajax-request

Leave a Reply

Your email address will not be published. Required fields are marked *

*

Time limit is exhausted. Please reload CAPTCHA.