[聚合文章] 同源策略&跨域请求

JSONP 2016-04-28 16 阅读

同源策略是浏览器最核心也是最基本的安全的功能。所谓同源,指的是协议、域名、端口号都相同,不同源的客户脚本(JavaScript)在没有明确授权的情况下,只能读写本域下的资源,不能读写对方的资源;

2. 什么是跨域?跨域有几种实现形式?

跨域是指突破JavaScript同源策略的限制, a.com 域名下的 js 可以操作 b.com 或者 child.a.com 下的对象中的数据。

实现形式:

  • 降域(最简单形式);对于主域相同子域不同( http://www.a.com/http://child.a.com )的情况,可以同时设置两个域名下 html 文件( index1.htmlindex2.html ) document.domain = a.com ,然后在 index1.html 中创建一个 iframe ,去控制 iframecontentDocument ,这样就实现了不同子域的跨域。 iframe相同主域 是其限制条件;

  • JSONP 跨域:由于直接用 XMLHttpRequest 请求不同域上的数据是不可以的。这种方式主要是通过动态插入一个 script 标签。浏览器对 script 的资源引用没有同源限制,同时资源加载到页面后会立即执行(没有阻塞的情况下)。

  • CORS(Cross-Origin Resource Sharing):跨域资源共享,IE需要10以上。

  • HTML5 postMessage

  • window.name

  • hash

3. jsonp 的原理是什么?

由于直接用 XMLHttpRequest 请求不同域上的数据是不可以的。这种方式主要是通过动态插入一个 script 标签。浏览器对 script 的资源引用没有同源限制,同时资源加载到页面后会立即执行(没有阻塞的情况下)

举个栗子说明:

a.com 域名下的 index.html 文件

<body>
<script src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>
<script type="text/javascript">
// jsonp跨域
function callback(json) {
console.log(json);
}
//动态创建一个script标签
var s = document.createElement('script');
//把a.json的URL赋给它
s.src = 'http://b.com/a.js';
//把这个script标签插入dom里
document.body.appendChild(s);
</script>

</body>

由于 jQuery 将 jsonp 封装在了 ajax 方法中,上面也可以写为:

<body>
<script src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>
<script type="text/javascript">
$.ajax({
url: 'http://b.com/a.js',
dataType: 'jsonp',
jsonpCallback: 'callback',
success: function (ret) {
console.log(ret);
}
});
</script>

</body>

b.com 域名下的 a.js 文件

callback({
"name": "aaa",
"age": 20
});

正常情况下是不能访问的,会报跨域错误,这里用到了 jsonp ,实现了跨域访问 a.js ,得到了 b.com 下的数据:

Object {name: "aaa", age: 20}

需要注意的是 jsonp 是 get 形式的,无法使用 post,承载的信息量有限,信息量较大时请使用更强大的 CORS。

4. CORS是什么?

CORS 全称是跨域资源共享(Cross-Origin Resource Sharing),是一种 ajax 跨域请求资源的方式,支持现代浏览器,IE支持10以上。

实现方式很简单,当你使用 XMLHttpRequest 发送请求时,浏览器发现该请求不符合同源策略,会给该请求加一个请求头: Origin ,后台进行一系列处理,如果确定接受请求则在返回结果中加入一个响应头: Access-Control-Allow-Origin ; 浏览器判断该相应头中是否包含 Origin 的值,如果有则浏览器会处理响应,我们就可以拿到响应数据,如果不包含浏览器直接驳回,这时我们无法拿到响应数据。所以 CORS 的表象是让你觉得它与同源的 ajax 请求没啥区别,代码完全一样。

【练习】

1. 本地搭建服务器,演示同源策略

  • 本地搭建服务器(如果使用 SAE 可创建不同的代码版本,这样可通过1.xxx.sinapp.com和2.xxx.sinapp.com 访问了)
  • 修改 本地host,通过不同域名访问本地服务器。比如访问 http://a.com/index.html , http://b.com/ajax.php ,本质是在 index.html 里使用 ajax 接口访问 http://b.com/ajax.php 里的数据。
  • 查看输出报错

通过 XAMPP 搭建本地服务器,编辑 host

  • 127.0.0.1 a.com
  • 127.0.0.1 b.com

这样通过 http://a.comhttp://b.com 两个不同域名可以访问本地服务器

编辑 http://a.com/index.html

<body>
<h1>a.com</h1>
<script src="http://apps.bdimg.com/libs/jquery/1.9.1/jquery.js"></script>
<script>
var start = 1;
$.ajax({
type: 'get',
url: '//b.com/ajax.php',
data: {
start: start,
len:5
},
success: function(ret) {
if(ret.status === 1){
alert('success to get data!');
start += 5;
}else {
alert('failed to get data!');
}
},
error: function(){
alert("Something error");
}
});
</script>

</body>

a.com 下我们通过 index.html 中的 ajax 访问 b.com 下的 ajax.php

编辑 http://b.com/ajax.php

<?php
// 后端 php 测试接口文件
$start = $_GET['start']; //1
$len = $_GET['len']; //5
$items = array();

for($i = 0; $i < $len; $i++){
array_push($items, '内容' . ($start+$i));
}
$ret = array('status'=>1, 'data'=>$items);

//{status: 1, data: ['内容1','内容2','内容3']}
sleep(0.5);
echo json_encode($ret);

浏览器输入 a.com ,看到报错信息:

2. 至少使用一种方式解决跨域问题

  • JSONP 解决跨域

a.com 下的 html 请求 b.com 下的 php

编辑 http://a.com/index.html

<script src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>
<script type="text/javascript">
$.ajax({
url: 'http://b.com/a.php',
dataType: 'jsonp',
success: function (ret) {
console.log("I'm " + ret.name + ",and I'm " + ret.age + " years old.");
},
error: function () {
alert("Something error");
}
});

编辑 http://b.com/a.php

<?php
$jsondata = '{
"name": "aaa",
"age": 20
}';
echo $_GET['callback'].'('.$jsondata.')';

result

I'm aaa,and I'm 20 years old.
  • CORS 解决跨域

a.com 下的 html 请求 b.com 下的 php

编辑 http://a.com/index.html

<script src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>
<script type="text/javascript">
$.ajax({
url: 'http://b.com/a.php',
// dataType: 'jsonp',
success: function (ret) {
console.log("I'm " + ret.name + ",and I'm " + ret.age + " years old.");
},
error: function () {
alert("Something error");
}
});

编辑 http://b.com/a.php

header("Access-Control-Allow-Origin:http://a.com");
<?php
$jsondata = '{
"name": "aaa",
"age": 20
}';
echo $_GET['callback'].'('.$jsondata.')';

result

I'm aaa,and I'm 20 years old.

注:本文内容来自互联网,旨在为开发者提供分享、交流的平台。如有涉及文章版权等事宜,请你联系站长进行处理。