详解Javascript几种跨域方式总结


Posted in Javascript onFebruary 27, 2017

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

1.window.name

window 对象的name属性是一个很特别的属性,当在 frame 中加载新页面时,name 的属性值依旧保持不变。那么我们可以在页面 A中用iframe加载其他域的页面B,而页面B中用JavaScript把需要传递的数据赋值给window.name,iframe加载完成之后,此时 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!';
// 这里是要传输的数据,大小一般为2M,IE和firefox下可以大至32M左右
// 数据格式可以自定义,如json、字符串
</script>

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

<script type="text/javascript">
var state = 0,
  iframe = document.createElement('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.createElement('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方法,以前一直以为jsonp是ajax的一种,后来才明白他们根本就不是一回事。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.html和http://script.a.com/b.html两个文件中分别加上 document.domain = "a.com";然后通过a.html文件中创建一个iframe,去控制iframe的contentDocument,这样两个js文件之间就可以 “交互”了。当然这种办法只能解决主域相同而二级域名不同的情况

www.a.com上的a.html

document.domain = 'a.com';
var ifr = document.createElement('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+、FireFox、Chrome、Opera等浏览器都已经支持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.createElement('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) {
  // ie、chrome的安全机制无法修改parent.location.hash,所以要利用一个中间的www.a.com域下的代理iframe
  var proxy = document.createElement('iframe');
  proxy.style.display = 'none';
  proxy.src = 'http://www.jack/c.html' + hash; // 注意该文件在"www.jack.com"域下
  document.body.appendChild(proxy);
}
window.onload = checkHash;

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
jQuery对象[0]是什么含义?
Jul 31 Javascript
document.getElementById的简写方式(获取id对象的简略写法)
Sep 10 Javascript
document.addEventListener使用介绍
Mar 07 Javascript
SeaJS 与 RequireJS 的差异对比
Dec 08 Javascript
AngularJS语法详解(续)
Jan 23 Javascript
js 获取元素在页面上的偏移量的方法汇总
Apr 13 Javascript
Javascript 实现计算器时间功能详解及实例(二)
Jan 08 Javascript
javascript实现右下角广告框效果
Feb 01 Javascript
Javascript for in的缺陷总结
Feb 03 Javascript
vue的toast弹窗组件实例详解
May 14 Javascript
Vue使用vue-draggable 插件在不同列表之间拖拽功能
Mar 12 Javascript
四十九个javascript小知识实用技巧
Nov 20 Javascript
JavaScript与JQUERY获取元素的宽、高和位置
Feb 26 #Javascript
JavaScript无阻塞加载和defer、async详解
Feb 26 #Javascript
浅谈JavaScript中的apply/call/bind和this的使用
Feb 26 #Javascript
JavaScript中Promise的使用详解
Feb 26 #Javascript
setTimeout函数的神奇使用
Feb 26 #Javascript
node.js入门学习之url模块
Feb 25 #Javascript
从零学习node.js之利用express搭建简易论坛(七)
Feb 25 #Javascript
You might like
php生成xml简单实例代码
2009/12/16 PHP
深入PHP autoload机制的详解
2013/06/09 PHP
删除PHP数组中的重复元素的实现代码
2017/04/10 PHP
PHP的RSA加密解密方法以及开发接口使用
2018/02/11 PHP
javascript时区函数介绍
2012/09/14 Javascript
使用GruntJS构建Web程序之构建篇
2014/06/04 Javascript
angularjs 处理多个异步请求方法汇总
2015/01/06 Javascript
AngularJS使用ng-Cloak阻止初始化闪烁问题的方法
2016/11/03 Javascript
基于jQuery实现图片推拉门动画效果的两种方法
2017/08/26 jQuery
vue 界面刷新数据被清除 localStorage的使用详解
2018/09/16 Javascript
jQuery使用each遍历循环的方法
2018/09/19 jQuery
Javascript迭代、递推、穷举、递归常用算法实例讲解
2019/02/01 Javascript
浅谈Webpack多页应用HMR卡住问题
2019/04/24 Javascript
使用Easyui实现查询条件的后端传递并自动刷新表格的两种方法
2019/09/09 Javascript
微信小程序实现点击图片放大预览
2019/10/21 Javascript
vue组件开发之slider组件使用详解
2020/08/21 Javascript
[04:00]黄浦江畔,再会英雄——完美世界DOTA2 TI9应援视频
2019/07/31 DOTA
详解Python中的条件判断语句
2015/05/14 Python
python集合用法实例分析
2015/05/30 Python
pandas值替换方法
2018/07/10 Python
python进行文件对比的方法
2018/12/24 Python
详解Python3 pickle模块用法
2019/09/16 Python
Django中自定义查询对象的具体使用
2019/10/13 Python
python图形用户接口实例详解
2019/12/16 Python
在python3中使用shuffle函数要注意的地方
2020/02/28 Python
python中shell执行知识点
2020/05/06 Python
公认8个效率最高的爬虫框架
2020/07/28 Python
上海某公司.net方向笔试题
2014/09/14 面试题
妇女干部培训方案
2014/05/12 职场文书
无毒社区工作方案
2014/05/23 职场文书
党风廉政建设调研报告
2015/01/01 职场文书
财务负责人岗位职责
2015/02/03 职场文书
党员转正申请报告
2015/05/15 职场文书
Javascript设计模式之原型模式详细
2021/10/05 Javascript
python基础之文件操作
2021/10/24 Python
如何通过简单的代码描述Angular父组件、子组件传值
2022/04/07 Javascript