0%

AJAX解决跨域问题(Access-Control-Allow-Origin)

之前遇到过跨域的问题,一直觉得很神秘,也没有多关注,就过去了,今天又看到几篇文章说跨域,闲来无事于是将其整理记录下来;

一些概念

先来阐述下几个概念:

跨域:是指浏览器对于JavaScript的同源策略限制,只要协议、域名、端口有任何一个不同,都被当作是不同的域,都不能执行或获取其他网站的资源;

姑且这么定义吧,举个简单例子,就是www.client.com网站上的程序不能从www.server.com网站上获取数据,如果强行获取,则会报出下面错误

image

有没有跨域,判断是不是属于跨域,可以参考下面:

URL 说明 是否允许通信
http://www.a.com/a.js 调用 http://www.a.com/b.js 同一域名下 允许
http://www.a.com/lab/a.js 调用 http://www.a.com/script/b.js 同一域名下不同文件夹 允许
http://www.a.com:8000/a.js 调用 http://www.a.com/b.js 同一域名,不同端口 不允许
http://www.a.com/a.js 调用 https://www.a.com/b.js 同一域名,不同协议 不允许
http://www.a.com/a.js 调用 http://70.32.92.74/b.js 域名和域名对应ip 不允许
http://www.a.com/a.js 调用 http://script.a.com/b.js 主域相同,子域不同 不允许
http://www.a.com/a.js 调用 http://a.com/b.js 同一域名,不同二级域名(同上) 不允许(cookie这种情况下也不允许访问)
http://www.cnblogs.com/a.js 调用 http://www.a.com/b.js 不同域名 不允许

CORS:CORS(Cross-Origin Resource Sharing)跨域资源共享,定义了必须在访问跨域资源时,浏览器与服务器应该如何沟通.CORS背后的基本思想就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是失败.

服务器端对于CORS的支持,主要就是通过设置Access-Control-Allow-Origin来进行的.如果浏览器检测到相应的设置,就可以允许Ajax进行跨域的访问.

解决方法

Solution 1:服务端程序解决

如果是双方预定沟通好请求允许数据,可以在服务端添加header头来解决

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

header( "Access-Control-Allow-Methods:POST,GET" );

看下面的例子:

客户端 www.client.com/cliend.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27


<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title> 跨域测试 </title>
<script src="//code.jquery.com/jquery-1.11.3.min.js"></script>
</head>

<body>
<button style="width:100px">click client</button>
<script type="text/javascript">
$("button").click(function () {
$.ajax({
url: "http://www.server.com/server.php",
type: "post",
data: {'text': 'hello world'},
success: function (msg) {
$("button").html(msg);
}

});
});
</script>
</body>
</html>

服务器端 www.server.com/server.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//允许所有域名获取数据
<?php
$text = $_POST['text'];
//允许所有的域名
header('content-type:application:json;charset=utf8');
header('Access-Control-Allow-Origin:*');
header('Access-Control-Allow-Methods:POST,GET');
header('Access-Control-Allow-Headers:x-requested-with,content-type');
echo json_encode($text);
?>

//允许制定域名获取数据
<?php
$text = $_POST['text'];
header('content-type:application:json;charset=utf8');
$origin = isset($_SERVER['HTTP_ORIGIN']) ? $_SERVER['HTTP_ORIGIN'] : '';
//允许指定域名
$allow_origin = [
'http://www.client.com',
'http://www.client2.com'
];
if (in_array($origin, $allow_origin)) {
header('Access-Control-Allow-Origin:' . $origin);
header('Access-Control-Allow-Methods:POST,GET');
header('Access-Control-Allow-Headers:x-requested-with,content-type');
}
echo json_encode($text);
?>

这样,理论上就可以解决跨域问题:

image

Solution 2:代理模式

解决思路:
例如 www.client.com/client.html 需要调用 www.server.com/server.php ,可以写一个接口 www.client.com/server.php ,由这个接口在后端去调用 www.server.com/server.php 并拿到返回值,然后再返回给index.html,这就是一个代理的模式.相当于绕过了浏览器端,自然就不存在跨域问题.

Solution 3:使用JSONP

使用之前,建议去看下我的另一篇文章Json和JsonP,然后再过来实践;

还是直接上代码:

客户端 www.client.com/client.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title> 跨域测试 </title>
<script src="//code.jquery.com/jquery-1.11.3.min.js"></script>
</head>

<body>
<button id="clickMe" style="width:100px">click get jsonP</button>
<script type="text/javascript">
$("#clickMe").click(function () {
$.ajax({
url: "http://www.server.com/jsonP.json",
type: "post",
dataType: "jsonP",
data: {'text': 'hello world'},
jsonpCallback: 'returnData', //可自定义 函数名
success: function (msg) {
alert(msg.text);
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert(XMLHttpRequest.status);
alert(XMLHttpRequest.readyState);
alert(textStatus);
}
});
});
</script>
</body>
</html>

服务器端 www.server.com/jsonP.json

1
returnData({"text":"hello jsonP"});

同样的也可以跨域获取数据

Solution 4:使用html5 API postMessage(转自这里)

客户端 www.client.com/client.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<iframe style="display: none" src="http://www.server.com/server.html" name="postIframe" onload="messageLoad()"></iframe>
<script>
function messageLoad() {
var url = "http://www.server.com";
window.postIframe.postMessage("给我tsort的信息", url); //发送数据
}
window.onmessage = function (e) {
e = e || event;
console.log(e.data); //接收b返回的数据,在控制台有两次输出
}
</script>
</body>
</html>

服务器端 www.server.com/server.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
window.onmessage = function(e){
e = e || event;
alert(e.data); //立即弹出a发送过来的数据
e.source.postMessage("好的,请稍等三秒!",e.origin); //立即回复a

var postData = {name:"tsrot",age:24};
var strData = JSON.stringify(postData); //json对象转化为字符串
setTimeout(function(){
e.source.postMessage(strData,e.origin);
},3000); //3秒后向a发送数据
}
</script>
</body>
</html>