浏览器同源政策以及JS跨域

浏览器同源政策以及JS跨域

同源是指协议相同、域名相同、端口相同。同源政策的目的,是为了保证用户信息的安全,防止恶意的网站窃取数据。javascript

同源策略主要限制下面三种状况html

  • Cookie 没法读取java

  • DOM 没法得到web

  • AJAX 请求不能发送json

同源策略的本意是为了保证用户的信息安全。但有时也会带来不便,下面咱们来看一下怎样规避同源的限制。segmentfault


Cookie

是服务器写入浏览器的一小段信息,只有同源的网页才能共享。api

当两个网页的一级域名相同,只是二级域名不一样的时候,咱们能够经过设置document.domain来共享cookie
具体操做以下:跨域

// 这两个网页的一级域名是相同的 
http://h1.test.com
http://h2.test.com

//为两个页面设置相同的 document.domain
document.domain = "test.com"

// 这样两个网页就能共享`Cookie`

document.domain 不能随意设置,只能把document.domain设置成自身或更高一级的父域。浏览器


跨域文档通讯

若是两个网页不一样源,就没法拿到对方的DOM,也没法进行通讯。典型的例子是iframe窗口和window.open方法打开的窗口,它们与父窗口没法通讯。安全

若是两个窗口一级域名相同,只是二级域名不一样,那么设置上一节介绍的document.domain属性,就能够规避同源政策,拿到DOM

关于通讯,咱们来看一下两种解决方案:

片断识别符

片断标识符(fragment identifier)指的是,URL#号后面的部分,若是只是改变片断标识符,页面不会从新刷新。

//父窗口能够把信息,写入子窗口的片断标识符
var src = originURL + '#' + data;
document.getElementById('myIFrame').src = src;

//子窗口经过监听hashchange事件获得通知
window.onhashchange = checkMessage;

function checkMessage() {
  var message = window.location.hash;
  // ...
}


//一样的,子窗口也能够改变父窗口的片断标识符
parent.location.href= target + "#" + hash;

window.postMessage

window.postMessage 是HTML5为了解决这个问题,引入了一个全新的API,不管两个窗口是否同源,都容许一个窗口向另外一个窗口发送数据。

语法:

// otherWindow 其余窗口的一个引用,好比iframe的contentWindow属性、执行window.open返回的窗口对象

//message 将要发送到其余 window的数据

//targetOrigin 接收消息的窗口的源(origin)

otherWindow.postMessage(message, targetOrigin)

其余window能够监听message

//监听 message 事件

window.addEventListener("message", receiveMessage, false);

//事件对象有一些经常使用的属性

//data 从其余 window 中传递过来的对象

//origin 消息发送方窗口的 origin

//source 对发送消息的窗口对象的引用

function receiveMessage(event){
    var origin = event.origin;
    
    //对发送消息的源进行验证
    
   if (origin !== "http://example.org:8080")
      return;

  // ...
}

实例:

窗口A : http://xiaoxiong.com
窗口B : http://miaomiao.com
显然这两个窗口不一样源,不能通讯,如今咱们用postMessage进行通讯。

//Awindow、Bwindow分别表示对 A B 窗口对象的引用
//B窗口向A窗口发消息
//若是A窗口的协议、主机地址或端口这三者的任意一项不匹配targetOrigin提供的值,消息就不能发送成功
//注意是用 Awindow 调用 postMessage 方法
 Awindow.postMessage("hello!","http://xiaoxiong.com");
 
 //在A中设置监听事件
 window.addEventListener("message", receiveMessage, false);
 
 function receiveMessage(event){
    console.log(event.origin);//http://miaomiao.com
    console.log(event.source);// Bwindow
    console.log(event.data);// hello!
    
}

AJAX

AJAX请求是咱们常常用到的异步请求方法,可是AJAX请求是不能跨域的。

下面咱们看一下常见的AJAX跨域方法

JSONP

基本思想是,网页经过添加一个<script>元素,向服务器请求JSON数据,这种作法不受同源政策限制;服务器收到请求后,将数据放在一个指定名字的回调函数里传回来。

function addScript(src) {
  var script = document.createElement('script');
  script.setAttribute("type","text/javascript");
  script.src = src;
  document.body.appendChild(script, body.firstChild);
}

window.onload = function () {
  //请求的查询字符串有一个callback参数,用来指定回调函数的名字,这对于JSONP是必需的
  addScript('http://test.com/a?callback=handler');
}

//请求回来数据做为回调函数的参数
//做为参数的JSON数据被视为JavaScript对象 不用进行转换
function handler(data) {
  console.log(data);
};

JSONP的优势:不受同源策略的限制;它的兼容性更好,在更加古老的浏览器中均可以运行;而且在请求完毕后能够经过调用callback的方式回传结果。

JSONP的缺点:它只支持GET请求而不支持POST等其它类型的HTTP请求;它只支持跨域HTTP请求这种状况,不能解决不一样域的两个页面之间如何进行JavaScript调用的问题。


WebSocket

WebSocket是一种通讯协议,使用ws://(非加密)和wss://(加密)做为协议前缀。该协议不实行同源政策,只要服务器支持,就能够经过它进行跨源通讯。

实例:

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com

上面代码中,有一个字段是Origin,表示该请求的请求源(origin),即发自哪一个域名。
正是由于有了Origin这个字段,因此WebSocket才没有实行同源政策。由于服务器能够根据这个字段,判断是否许可本次通讯。若是该域名在白名单内,服务器就会作出以下回应。


CORS(Cross-origin resource sharing)

跨域资源共享 是官方的跨域解决方案。
整个CORS通讯过程,都是浏览器自动完成,不须要用户参与。对于开发者来讲,CORS通讯与同源的AJAX通讯没有差异,代码彻底同样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感受。

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

基本流程

浏览器发现是跨源AJAX请求,浏览器就直接发出CORS请求。具体来讲,就是在头信息之中,增长一个Origin字段。

GET /cors HTTP/1.1
Origin: http://xiaoxiong.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

上面的头信息中,Origin字段用来讲明,本次请求来自哪一个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否赞成此次请求。
若是Origin指定的源,不在许可范围内,服务器会返回一个正常的HTTP回应。浏览器发现,这个回应的头信息没有包含Access-Control-Allow-Origin字段,就知道出错了,从而抛出一个错误,被XMLHttpRequestonerror回调函数捕获。注意,这种错误没法经过状态码识别,由于HTTP回应的状态码有多是200
若是Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段。

Access-Control-Allow-Origin: http://xiaoxiong.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8

Access-Control-Allow-Origin
该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求。


CORSJSONP对比,更为先进、方便和可靠。

一、 JSONP只能实现GET请求,而CORS支持全部类型的HTTP请求。

二、 使用CORS,开发者可使用普通的XMLHttpRequest发起请求和得到数据,比起JSONP有更好的错误处理。

三、 JSONP主要被老的浏览器支持,它们每每不支持CORS,而绝大多数现代浏览器都已经支持了CORS)。

四、 jsonp在调用失败的时候不会返回各类HTTP状态码。

五、在请求完毕后能够经过调用callback的方式回传结果。将回调方法的权限给了调用方。这个就至关于将controller层和view层终于分开了。我提供的jsonp服务只提供纯服务的数据,至于提供服务之后的页面渲染和后续view操做都由调用者来本身定义就行了。若是有两个页面须要渲染同一份数据,大家只须要有不一样的渲染逻辑就能够了,逻辑均可以使用同 一个jsonp服务。

参考文献

相关文章
相关标签/搜索