同源策略和跨域

同源策略

什么是同源策略

浏览器有一个很重要的概念——同源策略(Same-Origin Policy)。所谓同源是指,域名协议端口相同。不同源的客户端脚(javascript、ActionScript)本在没明确授权的情况下,不能读写对方的资源。


说到同源策略,就不得不说到一个URL(统一资源定位符)

url的组成
这里看到一个url的组成,就是上面这个部分。其中一般端口浏览器会默认给你加上去不同的协议名,端口不一样:

  • http的端口号是80
  • https的端口号是443,安全性高于https

这样同源策略就很好理解了:例如

1
2
3
4
5
http://liruihaod.github.io:80与下面网站是否同源
http://liruihaod.github.io/a/b.html //同源,只是在一个域名的不同目录

https://liruihaod.github.i0:80//不同源,协议不同,端口也不对。

http://liruihaod.git.io//不同源,域名不一样

同源策略的好处:

怎么说。。。就好比我现在登录支付宝或者网页网页的时候,打开了其他网页,如果这个网页上有恶意的javascript不就可以获取到我支付宝那个网页里面的数据了,想着就怕。


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

给我感觉。同源策略就和楚河汉界一样,各个网页之间约定不能跨域调用其他页面上的资源,我不来你这,你也别来我这。


那什么是跨域?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 不同域名 不允许

跨域的几种实现方法

举个例子:
我们在www.aaa.com/index.html这个文件中通过ajax 去访问这个目录下的另外一个php文件。
示意图
这样的是没问题的因为是同一域名下的不同子文件。

如果去访问bbb.aaa.com下面的php文件则访问不了,会出现错误
示例图
解决方法有

利用JSONP实现跨域

利用JSONP实现跨域原理:

  • 虽然因为同源策略,浏览器把跨域请求都禁止了。
  • 但是<script>标签里面是可以突破同源限制,来获取数据的。
  • 我们现在a.html里面设置一个回调函数,先在被引用的对象中存放好数据(一般格式为json),然后作为参数存放在事先约定好的回调函数中,然后a.html页面用script标签动态引用,然后执行回调函数,获得我们需要的数据。

例如:

1
2
3
4
5
6
7
8
9
10
<script scr="htpp://http://1.liruilushi.applinzi.com/level-31.php?callback=zone"></script>
//利用script标签是不受同源策略的影响,把数据写在url里面,这里callback告知后台回调函数名称告诉后台所以请把查询结果传入这个函数中进行调用。
这里没有写数据,如果有数据再后面加上就行,和get请求类似


<script>
function zone(code){
$("#text").val(code.data);
}
</script>

如果是jquery则只需要在dataType设置为jsonp
例如:

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
$(".button").on("click",function(){
$(this).html('<img src="http://cdn.uehtml.com/201402/1392662659967_1140x0.gif">')
$.ajax({
url:"http://1.liruilushi.applinzi.com/level-31.php",
dataType: 'jsonp',
type:"get",
jsonpCallback:"zone"
success:function(node){
onSuccess(node);
},
error:function(){
onError();
}
})
})

function onSuccess(node){
var a=node.data;
$("#text").val(a);
$(".button").text("跨域访问成功")
}
function onError(){
alert("出错了");
$(".button").text("跨域访问失败")
}
这里不写zone这个函数也行,jquery在处理jsonp类型的ajax时,自动帮你生成回调函数并把数据取出来供success属性方法来调用。不得不说jquery的功能还是很强大的

示意图

jsonp跨域的好处与弊端:
  • jsonp的好处:
    • 主要就是跨域请求了吧,兼容性好,在更古老的浏览器中都可以运行。
  • jsonp的弊端:
    • 因为jsonp本身是利用script标签中的src来进行跨域请求,就只支持get请求,不支持post请求,而get请求是有长度限制的。一般作用于少量数据的传输.
    • callback可能会被注入脚本,造成XSS攻击,解决办法:字符串过滤,限定jsonp的回调方法名的安全字符范围为(a-zA-Z0-9$ )
      • 存在安全问题,jsonp本身是通过src来请求数据,需要额外的身份校验。来保证数据只有通过身份校验(例如cookie)的才能获取数据。
      • 它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题。

降域

这个是针对两个域名必须属于一个同一个基础域名当然协议,端口这个是前提的前提。开始我也不懂,直到打开了QQ空间。
例如qzone.qq.ocm/a.htmluser.qq.com/b.html

具体做法是:

  • 两个文件都添加document.domain:liruilushi.applinzi.com这个是针对于ifeame标签

降域的弊端:
  • 有大前提,两个域名必须属于同一基础域名,且所用协议、端口相同.只针对于iframe类型跨域
  • document.domian只能声明为父级或者父级以上域名,父级域名不能声明子域名。“只能向上声明,不能向下申明”

设置CORS(跨域资源共享)来实现跨域

之前一个例子的报错信息上就能看到

示例图
大致意思就是.请求的这个网址里面的CORS设置没有将这个网址归纳在允许访问的域名里面。
这里要做的很简单,就是在之前那个PHP文件中设置下header
例如:

1
2
3
4
5
<?php
header("Access-Control-Allow-Origin:http://3.liruilushi.applinzi.com");
$tem = array("status"=>1, "data"=>"今天是星期五");

echo json_encode($tem);

好比一个是a.com一个是b.com,b.com发表一个申明:我允许a.com来访问了,然后a.com就发起对b.com的ajax请求,从而实现跨域。


CORS的好处:

  • 有别于JSONP,请求方式不限于get,另外因为不限于get请求,传输数据的大小不会受到url的限制,传输大量数据的时候建议使用CORS
  • 使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理。例如,错误的时候可以获取到状态码,而JSONP就不能
  • 不过只支持IE10,老浏览器还是只能用jsonp

使用window.name来进行跨域

我们设置a.html

1
2
3
4
5
6
7
<script>
window.name="A页面上面的Z-one"
setTimeout(function(){
window.location="http://1.liruihaod.applinzi.com/b.html"
},3000)
//window.location对象用于获得当前页面的地址 (URL),并把浏览器重定向到新的页面。
</script>

然后在另外一个域名下的b.html设置

1
2
3
<script>
alert(window.name)
</script>

结果网页在三秒后转到了b.html 并弹出来了我们想要的数据
示意图

我们看到在b.html页面上成功获取到了它的上一个页面a.html给window.name设置的值。如果在之后所有载入的页面都没对window.name进行修改的话,那么所有这些页面获取到的window.name的值都是a.html页面设置的那个值,

1
而 window.name正式利用window.name的这个特性:不会随着URI改变而变化,只会随着tab(窗口)的关闭而消失。


这样b.html页面就获得了a.html里面的数据,不过有点不好的就是网页会发生跳转,如果想让网页不发生跳转.可以借用iframe这个中间人.
具体代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<body>
//这里通过display:none使得iframe隐藏。
<iframe id="iframe" src="www.b.com/b.html" frameborder="0" ></iframe>
</body>
<script>
window.onload=function(){
var iframe=document.getElementById("iframe")
iframe.onload=function(){

var data=iframe.contentWindow.name//获取iframe里面window.name,也就是b.html页面给他设置的数据
alert(data);
}
iframe.src=a.html//这里改变iframe的URL。确保iframe与a.html同源,但是data并不会随着URL的改变而消失。

}
</script>


使用HTML5的window.postMessage方法跨域

window.postMessage(message,targetOrigin) 方法是html5新引进的特性,可以使用它来向其它的window对象发送消息,无论这个window对象是属于同源或不同源


调用postMessage方法的window对象是指要接收消息的那一个window对象,该方法的第一个参数message为要发送的消息,类型只能为字符串;第二个参数targetOrigin用来限定接收消息的那个window对象所在的域,如果不想限定域,可以使用通配符 * 。
postMessage(data,origin)


补充一点——JSON和JSONP到底什么关系?

  • ajax和jsonp这两种技术在调用方式上”看起来”很像,目的也一样,都是请求一个url,然后把服务器返回的数据进行处理,因此jquery和ext等框架都把jsonp作为ajax的一种形式进行了封装。

  • 但ajax和jsonp其实本质上是不同的东西。ajax的核心是通过XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加