首页 > 技术文章 > js跨域

superil 2017-09-20 16:41 原文

 第一次写博客,好紧张,不知道能写成啥样,哈哈哈。

自己的一知片解,有错请多多指教,嘻嘻嘻。

一、何为跨域?

只要协议、域名、端口后任何一个不同,就是跨域。

举个例子:

http://www.example.com 协议不同
https://www.example.com
http://www.example.com 域名不同
http://www.test.com
http://www.example.com 端口不同
http://www.example.com:81

 

 

 

 

 

 

注意:ip相同,域名不同,也是跨域。(在本地写demo时,配置host文件,127.0.0.1配置了两个域名,纠结是否算跨域,试了一下,是的)

二、跨域的类型

1.cookie

只有同源的网页才能共享cookie

2.iframe

网格网页不同源,无法拿到对方的DOM

3.ajax

只能请求同源的网址,否则报错

三、解决跨域

我在我电脑的host文件里配置了两个域名模拟跨域。

以下,test1代表www.test1.myhost.com; test2代表www.test2.myhost.com

1.cookie

存在这样一种情况能够实现共享cookie:两个网页一级域名形同,二级域名不同,设置cookie时,指定两个网页的domain相同,便可以共享cookie啦。

test1:

document.cookie = 'key1= value1; domain=myhost.com';
console.log(document.cookie);//"key1 = value1"

test2:

console.log(document.cookie);//"key1 = value1"

就酱紫,他俩共享cookie了,好可怕

打开application看看,发现这cookie原来在myhost.com域名下。

2.iframe

一个页面中嵌套一个iframe,这个iframe的src与主页面跨域。这样的话,主页面获取操作iframe的DOM,iframe也无法操作主页面的DOM

test1为主页面,test2为嵌入的iframe

解决iframe跨域的,宝宝只知道这三种:

(1)片段识别符

(2)window.name

(3)window.postMessage

2.1 片段识别符

指的是URL中#后面的部分,只改变片段识别符,页面不会重新刷新。

实现思路:

父窗口可以把要传递的数据写入iframe的src片段识别符中,iframe通过监听hashchange事件得到通知,获取数据

同样道理,子窗口也可以改变父窗口的片段识别符,达到同样的效果。示例来喽:

父→子:

主页面

var origin  = $('iframe').attr('src');
$('iframe').attr('src', origin + '#主窗口');

iframe

console.log(window.location.hash);//"#主窗口"

子→父:

iframe

parent.location.href ='http://www.test1.myhost.com:8080/tutor/cookie#iframe'

主页面

console.log(window.location.hash);//"#iframe"

2.2 window.name

浏览器窗口有window.name属性,它的特点是无论是否同源,只要在同一个窗口中,前一个网页设置了这个属性,后一个网页就可以读取它

实现思路:

将iframe需要与主页面传递的数据写到iframe的window.name中,完成后将iframe的src设置成与主页面同源,此时,主页面与iframe就同源了,就可以拿到对方的数据啦,而iframe的src更改并不影响window.name的值啊,主页面便可以轻松读取iframe中window.name的值啦,是不是很机智!再来个例子吧。

iframe

window.name = '哈哈哈,我是iframe';
location.href = 'http://www.test1.myhost.com:8080/这里就是一个与主页面同源的页面';

主页面

console.log($('iframe')[0].contentWindow.name);//"哈哈哈,我是iframe"

2.3 window.postMessage

html5引入的新API,允许跨窗口通信,不论这两个窗口是否同源。

window.postMessage(data, url); //data为要传给目标的数据,url为目标的url。

注意,此window是目标窗口的window,不是本窗口的window

同样来个例子:

子→父:

iframe

top.postMessage('hello', 'http://www.test1.myhost.com:8080/tutor/cookie');

主页面

window.addEventListener('message', function(e) {
  console.log(e.data);//"hello"
}, false);

父→子:

主页面

$('.post').on('click', function() {
   $('iframe')[0].contentWindow.postMessage('hello hello', 'http://www.test2.myhost.com:8080/这里就是一个目标url');
});

iframe

window.addEventListener('message', function(e) {
  console.log(e.data);//"hello hello"
}, false);

这里的e有几个重要属性:

a.data:传递来的message

b.source:发送消息的窗口对象

c.origin:发送消息窗口的源(协议+主机+端口号)

3.ajax

诶嘿,宝宝知道的也是三种,哈哈哈,好巧啊

(1)JSONP

(2)websocket

(3)CORS

分别说说吧

3.1 JSONP

原理就是利用script脚本拥有跨域能力。

基本思想是,网页添加一个script元素,src放需要请求的接口,这种做法不受同源策略的限制。服务器收到请求后,将数据放在一个指定的回调函数里传回来。

这种方法简单适用,老式的浏览器全部支持,服务器改造也非常小。

js实现:

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

$('.ajax').on('click', function() {
  addScriptTag('http://www.php.myhost.com/jsonp.php?callback=foo');
});

function foo(data) {
  console.log('response data is: ' + data);
};

jQuery实现:

$('.ajax').on('click', function() {
  $.ajax({
    url: 'http://www.php.myhost.com/jsonp.php',
    type: 'get',
    dataType: 'jsonp',
    jsonpCallback: 'foo',
    data: {}
  });
});

function foo(data) {
  console.log('Your response is: ' + data);
};

PHP代码:

<?php

$callback = $_GET['callback'];
$data = 'hello';
echo $callback.'('.json_encode($data).')';

?>

3.2 websocket

websocket是一种通信协议。该协议不实行同源策略,只要服务器支持,就可以通过它进行跨源通信。

例如,长连接呀

其实不太懂,就先不说它了哈

3.3 CORS

开始扯啦

CORS是跨源资源分享(Cross-Origin Resource Sharing)的缩写。它是W3C标准,是跨源AJAX请求的根本解决方法。

CORS请求分为两类:简单请求和非简单请求,太多了,这里就先不介绍了,一查就知道了。

简单请求就是浏览器发出CORS请求时,http头当中增加一个域(origin)的信息。该域包含协议名、地址及一个可选的端口,用来说明本次请求来自哪个源,服务器根据这个值决定是否同意这次请求。(这都是浏览器代为发送,开发者的代码无法触及到)

如果这个指定的源不在许可范围内,服务器就返回一个正常的http回应。但是回应中没有Access-Control-Allow-Origin字段,抛出异常,XMLHttpRequest的onerror捕获。

如果这个指定的源在许可范围内,会多出几个头信息字段(都以Access-Contrl-开头)

非简单请求(我平常请求的接口都是非简单请求,因为content-type为application/json)是在发起正式通信之前增加一次http查询,实现询问服务器当前网页所在域名是否在许可名单中,得到肯定答复后浏览器才会发出正式的XMLHttpRequest请求,否则报错。

预检请求的方式是options,头信息里面关键字段是origin,表示来自哪个源。服务器收到预检请求后检查origin,确认是否允许跨源请求,就可以做出回应了。

允许请求:在http回应中,关键是Access-Control-Allow-Origin字段,如:Access-Control-Allow-Origin:http://api.com,表示http://api.com可以请求数据。设为*,表示同意任何跨域。

不允许请求:会返回一个正常的http回应,但没有任何CORS相关的头信息字段,浏览器会认为不同意预检请求,触发一个错误,被XMLHttpRequest的onerror捕获。

总而言之,普通跨域请求,只要服务端设置Access-Control-Allow-Origin即可,前端无须设置。带cookie请求,需要前后端都设置字段。注意,所带cookie为跨域请求接口所在域的cookie,而非当前页的。

查了一下前端带cookie请求咋实现:

js

 var xhr = new XMLHttpRequest(); // 前端设置是否带cookie 
 xhr.withCredentials = true;

jquery

  $.ajax({
    xhrFields: {        
      withCredentials: true // 前端设置是否带cookie    
    },
  });

vue:

vue框架在vue-resource封装的ajax组件中加入以下代码:
Vue.http.options.credentials = true

ok ,总算写完啦~~~

哦,对了,加一下ajax跨域的几种现象(这几种现象这个博客说的很好:http://www.cnblogs.com/dailc/p/5893341.html#crossDomain_crosPrinciple)

推荐阅读