Swift51.com
麦子学院 头像
麦子学院  2017-02-27 18:26

Javascript学习之跨域详解

回复:0  查看:2211  

在客户端编程语言中如javascript,同源策略规定跨域之间的脚本是隔离的,一个域的脚本不能访问和操作另外一个域的绝大部分属性和方法。只有当两个域具有相同的协议,相同的主机,相同的端口时,我们就认定他们是相同的域。可是在实际开发中我们经常需要获取其他域的资源,这个时候各种不同的跨域资源方式就各显神通了,今天主要来总结一下工作中常用的几种跨域方式,以备查询。一起来看看吧,希望对大家学习javascript有所帮助。

  1.window.name

  window 对象的name属性是一个很特别的属性,当在 frame 中加载新页面时,name 的属性值依旧保持不变。那么我们可以在页面 A中用iframe加载其他域的页面B,而页面B中用JavaScript把需要传递的数据赋值给window.nameiframe加载完成之后,此时 name 属性值可被获取到,以访问 Web 服务发送的信息。但 name 属性仅对相同域名的 frame 可访问。这意味着为了访问 name 属性,当远程 Web 服务页面被加载后,必须导航 frame 回到原始域。即页面A修改iframe的地址,将其变成同域的一个地址,然后就可以读出window.name的值了。一旦 name 属性获得,销毁 frame 。这个方式非常适合单向的数据请求,而且协议简单、安全。

  页面B(www.jesse.com/data.html)代码如下:

  <script type="text/javascript">window.name = 'I was there!';// 这里是要传输的数据,大小一般为2MIEfirefox下可以大至32M左右// 数据格式可以自定义,如json、字符串script>

  页面A(www.jack.com/index.html)代码如下:

  <script type="text/javascript">var state = 0,

  iframe = document.('iframe'),

  loadfn = function() {

  if (state === 1) {

  var data = iframe.contentWindow.name; // 读取数据

  console.log(data); //弹出'I wasthere!'

  (function(){

  //获取数据以后销毁这个iframe

  iframe.contentWindow.document.write('');

  iframe.contentWindow.close();

  document.body.removeChild(iframe);

  })();

  } else if (state === 0) {

  state = 1;

  // 设置的代理页面使其回原始域

  iframe.contentWindow.location = "http://www.jack.com/proxy.html";;

  }

  };

  iframe.src = 'http://www.jesse.com/data.html';if (iframe.attachEvent) {

  iframe.attachEvent('onload', loadfn);

  } else {

  iframe.onload = loadfn;

  }document.body.appendChild(iframe);script>

  2.具备src的标签

  虽然浏览器默认禁止了跨域访问,但并不禁止在页面中用标签的src属性引用其他域的文件。根据这一点,可以方便地通过创建具有src属性的节点方法来实现完全跨域的通信。使用这种原理的跨域方式有以下几种:

  动态创建script

  例如我要从域A的页面pageA加载域B的数据,那么在域B的页面pageB中我以JavaScript的形式声明pageA需要的数据,然后在 pageA中用script标签把pageB加载进来,那么pageB中的脚本就会得以执行。

  pageA(www.jack.com/index.html)代码如下:

  function getData(data){

  //这里是对获取的数据的相关操作

  console.log(data);

  //数据获取到后移除创建的script标签

  document.body.removeChild(originData);

  }var originData = document.('script');

  originData.src = 'http://www.jesse.com/data.js';

  originData.setAttribute("type", "text/javascript");document.body.appendChild(originData);

  pageB(www.jesse.com/data.js)代码如下:

  getData('这里是远程跨域获取的数据');//数据格式可以自定义,如json、字符串

  jsonp

  在用$.ajax()获取远程数据时,如果是跨域资源则可以使用jsonp方法,以前一直以为jsonpajax的一种,后来才明白他们根本就不是一回事。ajax是以xhr方式请求数据的,而jsonp是以script方式请求数据的,这个就是和上面的动态创建script方式一样。

  pageA(www.jack.com/index.html)代码如下:

  $.ajax({

  //JSONP不支持POST方式

  type:"GET",

  url:"http://www.jesse.com/data.php";,

  dataType:"jsonp",

  //自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名,也可以写"?"jQuery会自动为你处理数据

  jsonpCallback:"getData",

  success: function(data){

  console.log(data);

  },

  error: function(){

  console.log('fail');

  }

  })

  pageB(www.jesse.com/data.js)代码如下:

  <?php

  $callback = $_GET['callback'];//得到回调函数名,这里是getData

  $data = array('a','b','c');//要返回的数据

  echo $callback.'('.json_encode($data).')';//输出?>

  3.document.domain

  对于主域相同而子域不同的例子,可以通过设置document.domain的办法来解决。 具体的做法是可以在http://www.a.com/a.htmlhttp://script.a.com/b.html两个文件中分别加上 document.domain = "a.com";然后通过a.html文件中创建一个iframe,去控制iframecontentDocument,这样两个js文件之间就可以 交互了。当然这种办法只能解决主域相同而二级域名不同的情况

  www.a.com上的a.html

  document.domain = 'a.com';var ifr = document.('iframe');

  ifr.src = 'http://script.a.com/b.html';

  ifr.style.display = 'none';document.body.appendChild(ifr);

  ifr.onload = function(){

  var doc = ifr.contentDocument || ifr.contentWindow.document;

  // 在这里操纵b.html

  alert(doc.getElementsByTagName("h1")[0].childNodes[0].nodeValue);

  };

  script.a.com上的b.html

  document.domain = 'a.com';

  4.跨域资源共享(CORS

  原理:跨源资源共享(CORS)定义一种跨域访问的机制,可以让AJAX实现跨域访问。CORS允许一个域上的网络应用向另一个域提交跨域AJAX请求。实现此功能非常简单,只需由服务器发送一个响应标头即可。它是通过客户端+服务端协作声明的方式来确保请求安全的。服务端会在HTTP请求头中增加一系列HTTP请求参数(例如Access-Control-Allow-Origin),来限制哪些域的请求和哪些请求类型可以接受,而客户端在发起请求时必须声明自己的源(Orgin),否则服务器将不予处理,如果客户端不作声明,请求甚至会被浏览器直接拦截都到不了服务端。服务端收到HTTP请求后会进行域的比较,只有同域的请求才会处理。

  pageA(www.jack.com/index.html)代码如下:

  var xhr = new XMLHttpRequest();

  xhr.onreadystatechange = function(){

  if(xhr.readyState === 4 && xhr.status === 200){

  console.log(xhr.responseText);

  }

  };

  xhr.open("get","http://www.jesse.com/data.php";);

  xhr.send(null);

  pageB(www.jesse.com/data.php)代码如下:

  <?php

  header("Access-Control-Allow-Origin: http://www.jack.com";);//与简单的请求相同

  header("Access-Control-Allow-Methods: GET, POST");//允许请求的方法

  header("Access-Control-Max-Age: 3628800"); //将这个请求缓存多长时间

  $data = array('a','b','c');//要返回的数据

  echo json_encode($data);//输出?>

  5.window.postMesage 不常用

  window.postMessage(message,targetOrigin) 方法是html5新引进的特性,可以使用它来向其它的window对象发送消息,无论这个window对象是属于同源或不同源,目前IE8+FireFoxChromeOpera等浏览器都已经支持window.postMessage方法。

  pageA(www.jack.com/index.html)代码如下:

  <iframe id="proxy" src="http://www.jesse.com/index.html"; onload="postMsg()" style="display: none">iframe><script type="text/javascript">var obj = {

  msg: 'hello world'

  }

  function postMsg() {

  var iframe = document.getElementById('proxy');

  var win = iframe.contentWindow;

  win.postMessage(obj, 'http://www.jesse.com');

  }script>

  pageB(www.jesse.com/data.php)代码如下:

  <script type="text/javascript">window.onmessage = function(e) {

  console.log(e.data.msg + " from " + e.origin);

  }script>

  6. location.hash 不常用

  pageA(www.jack.com/index.html)代码如下:

  function startRequest() {

  var ifr = document.('iframe');

  ifr.style.display = 'none';

  ifr.src = 'http://www.jesse.com/b.html#sayHi'; //传递的location.hash

  document.body.appendChild(ifr);

  }

  function checkHash() {

  try {

  var data = location.hash ? location.hash.substring(1) : '';

  if (console.log) {

  console.log('Now the data is ' + data);

  }

  } catch (e) {};

  }

  setInterval(checkHash, 5000);window.onload = startRequest;

  pageA(www.jack.com/proxy.html)代码如下:

  parent.parent.location.hash = self.location.hash.substring(1);

  pageB(www.jesse.com/b.html)代码如下:

  function checkHash() {

  var data = '';

  //模拟一个简单的参数处理操作

  switch (location.hash) {

  case '#sayHello':

  data = 'HelloWorld';

  break;

  case '#sayHi':

  data = 'HiWorld';

  break;

  default:

  break;

  }

  data && callBack('#' + data);

  }

  function callBack(hash) {

  // iechrome的安全机制无法修改parent.location.hash,所以要利用一个中间的www.a.com域下的代理iframe

  var proxy = document.('iframe');

  proxy.style.display = 'none';

  proxy.src = 'http://www.jack/c.html' + hash; // 注意该文件在"www.jack.com";域下

  document.body.appendChild(proxy);

  }window.onload = checkHash;

 

 

来源:博客园