解决前端跨域问题方案汇总


Posted in Javascript onNovember 20, 2016

1.同源策略如下:

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 不同域名 不允许

特别注意两点:

第一,如果是协议和端口造成的跨域问题“前台”是无能为力的,
第二:在跨域问题上,域仅仅是通过“URL的首部”来识别而不会去尝试判断相同的ip地址对应着两个域或两个域是否在同一个ip上。
“URL的首部”指window.location.protocol +window.location.host,也可以理解为“Domains, protocols and ports must match”。

2. 前端解决跨域问题

1> document.domain + iframe      (只有在主域相同的时候才能使用该方法)

1) 在www.a.com/a.html中:

document.domain = 'a.com';
var ifr = document.createElement('iframe');
ifr.src = 'http://www.script.a.com/b.html';
ifr.display = none;
document.body.appendChild(ifr);
ifr.onload = function(){
  var doc = ifr.contentDocument || ifr.contentWindow.document;
  //在这里操作doc,也就是b.html
  ifr.onload = null;
};

2) 在www.script.a.com/b.html中:

document.domain = 'a.com';

2> 动态创建script

这个没什么好说的,因为script标签不受同源策略的限制。

JavaScript

function loadScript(url, func) {
 var head = document.head || document.getElementByTagName('head')[0];
 var script = document.createElement('script');
 script.src = url;
 
 script.onload = script.onreadystatechange = function(){
  if(!this.readyState || this.readyState=='loaded' || this.readyState=='complete'){
   func();
   script.onload = script.onreadystatechange = null;
  }
 };
 
 head.insertBefore(script, 0);
}
window.baidu = {
 sug: function(data){
  console.log(data);
 }
}
loadScript('http://suggestion.baidu.com/su?wd=w',function(){console.log('loaded')});

//我们请求的内容在哪里?
//我们可以在chorme调试面板的source中看到script引入的内容

3> location.hash + iframe

原理是利用location.hash来进行传值。

假设域名a.com下的文件cs1.html要和cnblogs.com域名下的cs2.html传递信息。
1) cs1.html首先创建自动创建一个隐藏的iframe,iframe的src指向cnblogs.com域名下的cs2.html页面
2) cs2.html响应请求后再将通过修改cs1.html的hash值来传递数据
3) 同时在cs1.html上加一个定时器,隔一段时间来判断location.hash的值有没有变化,一旦有变化则获取获取hash值
注:由于两个页面不在同一个域下IE、Chrome不允许修改parent.location.hash的值,所以要借助于a.com域名下的一个代理iframe

代码如下:
先是a.com下的文件cs1.html文件:

function startRequest(){
  var ifr = document.createElement('iframe');
  ifr.style.display = 'none';
  ifr.src = 'http://www.cnblogs.com/lab/cscript/cs2.html#paramdo';
  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, 2000);

cnblogs.com域名下的cs2.html:

//模拟一个简单的参数处理操作
switch(location.hash){
  case '#paramdo':
    callBack();
    break;
  case '#paramset':
    //do something……
    break;
}
 
function callBack(){
  try {
    parent.location.hash = 'somedata';
  } catch (e) {
    // ie、chrome的安全机制无法修改parent.location.hash,
    // 所以要利用一个中间的cnblogs域下的代理iframe
    var ifrproxy = document.createElement('iframe');
    ifrproxy.style.display = 'none';
    ifrproxy.src = 'http://a.com/test/cscript/cs3.html#somedata';  // 注意该文件在"a.com"域下
    document.body.appendChild(ifrproxy);
  }
}

a.com下的域名cs3.html

//因为parent.parent和自身属于同一个域,所以可以改变其location.hash的值
parent.parent.location.hash = self.location.hash.substring(1);

4> window.name + iframe

window.name 的美妙之处:name 值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name 值(2MB)。

1) 创建a.com/cs1.html

2) 创建a.com/proxy.html,并加入如下代码

<head>
 <script>
 function proxy(url, func){
  var isFirst = true,
    ifr = document.createElement('iframe'),
    loadFunc = function(){
     if(isFirst){
      ifr.contentWindow.location = 'http://a.com/cs1.html';
      isFirst = false;
     }else{
      func(ifr.contentWindow.name);
      ifr.contentWindow.close();
      document.body.removeChild(ifr);
      ifr.src = '';
      ifr = null;
     }
    };
 
  ifr.src = url;
  ifr.style.display = 'none';
  if(ifr.attachEvent) ifr.attachEvent('onload', loadFunc);
  else ifr.onload = loadFunc;
 
  document.body.appendChild(iframe);
 }
</script>
</head>
<body>
 <script>
  proxy('http://www.baidu.com/', function(data){
   console.log(data);
  });
 </script>
</body>

3) 在b.com/cs1.html中包含:

<script>
  window.name = '要传送的内容';
</script>

5> postMessage(HTML5中的XMLHttpRequest Level 2中的API)

1) a.com/index.html中的代码:

<iframe id="ifr" src="b.com/index.html"></iframe>
<script type="text/javascript">
window.onload = function() {
  var ifr = document.getElementById('ifr');
  var targetOrigin = 'http://b.com'; // 若写成'http://b.com/c/proxy.html'效果一样
                    // 若写成'http://c.com'就不会执行postMessage了
  ifr.contentWindow.postMessage('I was there!', targetOrigin);
};
</script>

2) b.com/index.html中的代码:

<script type="text/javascript">
  window.addEventListener('message', function(event){
    // 通过origin属性判断消息来源地址
    if (event.origin == 'http://a.com') {
      alert(event.data);  // 弹出"I was there!"
      alert(event.source); // 对a.com、index.html中window对象的引用
                 // 但由于同源策略,这里event.source不可以访问window对象
    }
  }, false);
</script>

6> CORS

CORS背后的思想,就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,还是应该失败。

IE中对CORS的实现是xdr

var xdr = new XDomainRequest();
xdr.onload = function(){
  console.log(xdr.responseText);
}
xdr.open('get', 'http://www.baidu.com');
......
xdr.send(null);

其它浏览器中的实现就在xhr中

var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
  if(xhr.readyState == 4){
    if(xhr.status >= 200 && xhr.status ){
      console.log(xhr.responseText);
    }
  }
}
xhr.open('get', 'http://www.baidu.com');
......
xhr.send(null);

实现跨浏览器的CORS

function createCORS(method, url){
  var xhr = new XMLHttpRequest();
  if('withCredentials' in xhr){
    xhr.open(method, url, true);
  }else if(typeof XDomainRequest != 'undefined'){
    var xhr = new XDomainRequest();
    xhr.open(method, url);
  }else{
    xhr = null;
  }
  return xhr;
}
var request = createCORS('get', 'http://www.baidu.com');
if(request){
  request.onload = function(){
    ......
  };
  request.send();
}

7> JSONP

JSONP包含两部分:回调函数和数据。

回调函数是当响应到来时要放在当前页面被调用的函数。

数据就是传入回调函数中的json数据,也就是回调函数的参数了。

function handleResponse(response){
  console.log('The responsed data is: '+response.data);
}
var script = document.createElement('script');
script.src = 'http://www.baidu.com/json/?callback=handleResponse';
document.body.insertBefore(script, document.body.firstChild);
/*handleResonse({"data": "zhe"})*/
//原理如下:
//当我们通过script标签请求时
//后台就会根据相应的参数(json,handleResponse)
//来生成相应的json数据(handleResponse({"data": "zhe"}))
//最后这个返回的json数据(代码)就会被放在当前js文件中被执行
//至此跨域通信完成

jsonp虽然很简单,但是有如下缺点:

1)安全问题(请求代码中可能存在安全隐患)

2)要确定jsonp请求是否失败并不容易

8> web sockets

web sockets是一种浏览器的API,它的目标是在一个单独的持久连接上提供全双工、双向通信。(同源策略对web sockets不适用)

web sockets原理:在JS创建了web socket之后,会有一个HTTP请求发送到浏览器以发起连接。取得服务器响应后,建立的连接会使用HTTP升级从HTTP协议交换为web sockt协议。

只有在支持web socket协议的服务器上才能正常工作。

var socket = new WebSockt('ws://www.baidu.com');//http->ws; https->wss
socket.send('hello WebSockt');
socket.onmessage = function(event){
  var data = event.data;
}
Javascript 相关文章推荐
一段批量给页面上的控件赋值js
Jun 19 Javascript
基于jquery的监控数据是否发生改变
Apr 11 Javascript
将list转换为json失败的原因
Dec 17 Javascript
js不能获取隐藏的div的宽度只能先显示后获取
Sep 04 Javascript
javascript获取flash版本号的方法
Nov 20 Javascript
JQuery 传送中文乱码问题的简单解决办法
May 24 Javascript
JavaScript动态添加css样式和script标签
Jul 19 Javascript
详解jquery选择器的原理
Aug 01 jQuery
get  post jsonp三种数据交互形式实例详解
Aug 25 Javascript
jquery 键盘事件的使用方法详解
Sep 13 jQuery
将jquery.qqFace.js表情转换成微信的字符码
Dec 01 jQuery
JavaScript 下载svg图片为png格式
Jun 21 Javascript
jQuery 的 ready()的纯js替代方法
Nov 20 #Javascript
node+experss实现爬取电影天堂爬虫
Nov 20 #Javascript
JSP防止网页刷新重复提交数据的几种方法
Nov 19 #Javascript
bootstrap datetimepicker2.3.11时间插件使用
Nov 19 #Javascript
js 定位到某个锚点的方法
Nov 19 #Javascript
js 模仿锚点定位的实现方法
Nov 19 #Javascript
Javascript使用function创建类的两种方法(推荐)
Nov 19 #Javascript
You might like
去除php注释和去除空格函数分享
2014/03/13 PHP
关于IE、Firefox、Opera页面呈现异同 写脚本很痛苦
2009/08/28 Javascript
event.X和event.clientX的区别分析
2011/10/06 Javascript
jQuery UI Autocomplete 体验分享
2012/02/14 Javascript
用js调用迅雷下载代码的二种方法
2013/04/15 Javascript
JS 打印功能代码可实现打印预览、打印设置等
2014/10/31 Javascript
VS2008中使用JavaScript调用WebServices
2014/12/18 Javascript
js实现下拉列表选中某个值的方法(3种方法)
2015/12/17 Javascript
js下拉菜单生成器dropMenu使用方法详解
2017/08/01 Javascript
AnglarJs中的上拉加载实现代码
2018/02/08 Javascript
详解Vue底部导航栏组件
2019/05/02 Javascript
Vue使用富文本编辑器Vue-Quill-Editor(含图片自定义上传服务、清除复制粘贴样式等)
2020/05/15 Javascript
vue制作toast组件npm包示例代码
2020/10/29 Javascript
Django在Win7下的安装及创建项目hello word简明教程
2014/07/14 Python
python中的sort方法使用详解
2014/07/25 Python
Python学习pygal绘制线图代码分享
2017/12/09 Python
Python爬取附近餐馆信息代码示例
2017/12/09 Python
Python实现的爬取网易动态评论操作示例
2018/06/06 Python
根据DataFrame某一列的值来选择具体的某一行方法
2018/07/03 Python
详解Python requests 超时和重试的方法
2018/12/18 Python
tensorflow 保存模型和取出中间权重例子
2020/01/24 Python
python中使用input()函数获取用户输入值方式
2020/05/03 Python
使用css3背景渐变中的透明度来设置不同颜色的背景渐变
2014/03/31 HTML / CSS
法国春天百货官网:Printemps.com
2020/06/29 全球购物
道德模范先进事迹
2014/02/14 职场文书
土建工程师岗位职责
2014/06/10 职场文书
体育系毕业生自荐信
2014/06/28 职场文书
思想作风纪律整顿心得体会
2014/09/04 职场文书
学校班子个人对照检查材料思想汇报
2014/09/27 职场文书
教师四风自我剖析材料
2014/09/30 职场文书
2014年招生工作总结
2014/11/26 职场文书
天那边观后感
2015/06/09 职场文书
国际贸易实训总结
2015/08/03 职场文书
强烈推荐:小学生:暑假作息时间表(值得收藏)
2019/07/09 职场文书
Java8 Stream API 提供了一种高效且易于使用的处理数据的方式
2022/04/13 Java/Android
mysql 体系结构和存储引擎介绍
2022/05/06 MySQL