1、跨域请求:
Cross Domain Request:跨域名的HTTP请求,浏览器从某个域名下的资源访问了另一域名下的另一资源(协议、域名或是端口号不同); ①浏览器允许跨域请求的情形: <img>、<link>、<script>、<iframe> ②禁止跨域请求的情形: XHR——浏览器默认出于安全考虑,禁止XHR跨域请求; ③相关名词及解决方案: a、相同域:两个具有相同的协议(如:http)、相同的端口(如:80)、相同的host(主机名),即为相同域,否则构成跨域; b、file协议:用于访问本地计算机中的文件; c、同源策略:跨域之间的脚本是隔离的,一个域中的脚本不能访问、操作另一个域的绝大部分属性和方法;同源策略应对一些特殊情况做处理,例:限制file协议下脚本的访问权限,本地的HTML文件在浏览器中是通过file协议打开的,若脚本能通过file协议访问到硬盘上的其他任意文件,就会出现安全隐患; d、单向跨域之JSONP:JSON with Padding; 原理:由于HTML中的script标签可以加载执行其它域的javascript,所以可以通过script标记动态加载其它域的资源; 实现:在pageB中以javascript的形式声明pageA所需的数据,然后在pageA中用script标签将 pageB加载进来,JSONP在此基础上加入回调函数,pageB加载完后会执行pageA中定义的函数,所需数据以参数形式传递给该函数; 限制:JSONP适合在受信任的双方传递数据,否则易被第三方脚本篡改页面内容,截获敏感数据; 代码: 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文件中被执行 //至此跨域通信完成 e、单向跨域之flash URLLoader: 原理:flash有自己的安全策略,服务器可以通过crossdomain.xml文件声明能被哪些域的SWF文件访问,SWF也可以通过API确定自身能被哪些域的SWF加载,跨域访问时,可借助flash发送HTTP请求; 实现:先修改域中crossdomain.xml,将其加入白名单,然后通过Flash URLLoader发送HTTP请求,通过Flash API将响应结果传递给javascript; 限制:不支持IOS; f、单向跨域之Access Control: 原理:浏览器发送跨域的HTTP请求,请求包含一个Access-Control-Allow-Origin的HTTP响应头部,该响应头声明了请求域的可访问权限; 实现:给被跨域访问的资源添加响应消息头部,设定允许来自某个域名下的页面访问当前页面:header('Access-Control-Allow-Origin:http://xxx.xx'); 限制:少数浏览器支持(Firefox、Chrome通过XMLHttpRequest,IE8通过XDomainRequest发送请求); g、单向跨域之window.name: 原理:当window的location变化,重新加载,name属性依然保持不变; 实现:在pageA中用iframe加载其它域的pageB,在pageB中用javascript把需要传递的数据赋值给window.name,iframe加载完成后,pageA修改iframe的地址为同域的一个地址,即可读出window.name的值; 代码: 请求代理: <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> 响应: <script> window.name = '要传送的内容'; </script> h、单向跨域之server proxy: 原理:在数据提供方没有提供对JSONP或window.name协议的支持,也没有对其它域开放访问权限时,可使用server proxy(服务器代理)方式抓取数据,跨域的HTTP请求在服务器端进行,客户端不用产生跨域的Ajax请求; 实现:在服务器配置一个代理,将Ajax请求绑定到这个代理路径下,然后由这个代理发送Http请求去访问文件; 限制:该跨域方式不需要和目标资源签订协议,带有侵略性,另需对该代理实施一定程度的保护,如限制他人使用或使用频率; i、双向跨域之document.domain: 原理:同域策略认为域和子域属于不同的域,修改document的domain属性为同一host,可以在域和子域或不同子域间通信; 实现:将不同子域document的domain属性修改为同一host,浏览器会认为它们处于同一域下,此时即可互相调用对方的method来通信; 代码: 请求: 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; }; 响应: document.domain = 'a.com'; j、双向跨域之FIM: 原理:Fragment Identiter Messaging,父窗口与iframe可以相互读写URL,URL中“#”号及其后面的字符被称为frag,它一般用于浏览器锚点定位,HTTP请求中不会携带frag,每个window通过改变其他window的location来发送消息,并通过监听自己的URL变化来接收消息; 限制:这种方式通信会产生不必要的浏览器历史记录,且有些浏览器不支持onhashchange事件,需要用轮询来获知URL的改变,另URL在浏览器下有长度限制,制约了每次传送的数据量; k、双向跨域之Flash LocalConnection: 原理:Flash API中有LocalConnection类,该类允许两个SWF之间进行通信,SWF可以播放在独立的Flash Player或者AIR中,也可以嵌套在HTML页面或者PDF中; 实现:在不同域的HTML页面各自嵌套一个SWF来相互传递数据; 限制:数据量有40kb的大小限制,且过程复杂,实用性不强; l、双向跨域之window.postMessage: HTML5定义的新方法,可跨window通信,在较旧浏览器中无法使用; 代码: 请求: <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> 响应: <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> m、双向跨域之Cross Frame: 原理:FIM的变种,借助空白iframe,不会产生多余浏览器历史记录,也不需要轮询URL的改变;域中pageA和空白代理页proxyA,另一个域pageB和其空白代理页proxyB,pageA向pageB发送消息时页面创建隐藏的iframe,iframe的src指向proxyB,并把message作为URL frag;pageB和proxyB是同域,iframe加载完成后,pageB可以获得iframe的URL,解析出message,并移除iframe;反向同理; 限制:Opera无法使用,但可使用window.postMessage; n、动态创建script: 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引入的内容 o、Web Socket: 原理:web sockets是一种浏览器的API,它的目标是在一个单独的持久连接上提供全双工、双向通信,在JS创建了web socket之后,会有一个HTTP请求发送到浏览器以发起连接。取得服务器响应后,建立的连接会使用HTTP升级从HTTP协议交换为web sockt协议; 代码: var socket = new WebSockt('ws://www.baidu.com');//http->ws; https->wss socket.send('hello WebSockt'); socket.onmessage = function(event){ var data = event.data; } 限制:只有在支持web socket协议的服务器上才能正常工作;2、jQuery中发起JSONP请求的方法: ①$.getJSON两种方法: a、使用XHR接收服务器端返回的JSON响应: $.getJSON('x.php',function(obj){}); b、使用script标签执行服务器返回的script响应——JSONP: $.getJSON('http://其他域名/x.php?callback=?',doResponse); ②$.ajax的两种方法: a、使用XHR接收服务器端返回的响应: $.ajax({}); b、使用script标签执行服务器返回的script响应——JSONP: $.ajax({ type:'GET', url:'x.php', data:{k:v}, dataType:'jsonp',//设定服务器端返回JSONP数据 success:fn });* 本篇内容部分自网上整理而来,请原作者勿怪!