前端跨域问题相关知识详解(原生js和jquery两种方法实现jsonp跨域)
1、同源策略
同源策略(Same origin policy),它是由Netscape提出的一个著名的安全策略。同源策略是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现,现在所有支持JavaScript 的浏览器都会使用这个策略。
所谓同源,就是指两个页面具有相同的协议,主机(也常说域名),端口,三个要素缺一不可。
所谓同源策略,指的是浏览器对不同源的脚本或者文本的访问方式进行的限制。即a.com 域名下的js无法操作b.com或是c.com域名下的对象。详细见下表:
URL1 | URL2 | 说明 | 是否允许通信 |
---|---|---|---|
http://www.foo.com/js/a.js | http://www.foo.com/js/b.js | 协议、域名、端口都相同 | 允许 |
http://www.foo.com/js/a.js | http://www.foo.com:8888/js/b.js | 协议、域名相同,端口不同 | 不允许 |
https://www.foo.com/js/a.js | http://www.foo.com/js/b.js | 主机、域名相同,协议不同 | 不允许 |
http://www.foo.com/js/a.js | http://www.bar.com/js/b.js | 协议、端口相同,域名不同 | 不允许 |
http://www.foo.com/js/a.js | http://foo.com/js/b.js | 协议、端口相同,主域名相同,子域名不同 | 不允许 |
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 |
不同域名 | 不允许 |
- eg:当浏览器的百度tab页执行一个脚本的时候会检查这个脚本是属于哪个页面的,即检查是否同源,只有和百度同源的脚本才会被执行。如果非同源,那么在请求数据时,浏览器会在控制台中报一个异常,提示拒绝访问。
- 特别注意两点:
- 第一,如果是协议和端口造成的跨域问题“前台”是无能为力的;
- 第二:在跨域问题上,域仅仅是通过“URL的首部”来识别而不会去尝试判断相同的ip地址对应着两个域或两个域是否在同一个ip上。
“URL的首部”指window.location.protocol +window.location.host,也可以理解为“Domains, protocols and ports must match”。
前端所说的跨域一般指“前台”处理跨域的办法,后台proxy这种方案牵涉到后台配置,有兴趣的可以参考yahoo的这篇文章:《JavaScript: Use a Web Proxy for Cross-Domain XMLHttpRequest Calls》
同源策略限制了不同源之间的交互,可我们平时文件中引用其他域名的js文件,css文件,图片文件为何没受到限制呢?同源策略限制的不同源之间的交互主要针对的是js中的XMLHttpRequest等请求,下面这些情况是完全不受同源策略限制的:
- 页面中的链接,重定向以及表单提交是不会受到同源策略限制的。链接就不用说了,导航网站上的链接都是链接到其他站点的。而你在域名
www.foo.com
下面提交一个表单到www.bar.com
是完全可以的。 - 跨域资源嵌入是允许的,当然,浏览器限制了Javascript不能读写加载的内容。如前面提到的嵌入的
<script src="..."></script>,<img>,<link>,<iframe>等
。当然,如果要阻止iframe嵌入我们网站的资源(页面或者js等),我们可以在web服务器加上一个X-Frame-Options DENY
头部来限制。nginx就可以这样设置add_header X-Frame-Options DENY;
。
互联网的许多网站之间图片相互盗链,A网站网页的img.src直接链接到B网站的图片地址,就是因为这个原因:<img>的src(获取图片),<link>的href(获取css),<script>的src(获取javascript)这三个都不符合同源策略,它们可以跨域获取数据。因此,你可以直接从一些cdn上获取jQuery,并且你网站上的图片也随时可能被别人盗用。
而我们的第一种跨域方法jsonp,就是因为<script>的src不符合同源策略而来的。
2、解决跨域问题-JSONP
Asynchronous JavaScript and XML (Ajax ) 是驱动新一代 Web 站点(流行术语为 Web 2.0 站点)的关键技术。Ajax 允许在不干扰 Web 应用程序的显示和行为的情况下在后台进行数据检索。使用 XMLHttpRequest
函数获取数据,它是一种 API,允许客户端 JavaScript 通过 HTTP 连接到远程服务器。Ajax 也是许多 mashup 的驱动力,它可将来自多个地方的内容集成为单一Web 应用程序。
不过,由于受到浏览器的限制,该方法不允许跨域通信。
即:当我们利用XMLHttpRequest对象从本地服务器获取数据时是可以的,但是它不允许跨服务器发送请求。既然XMLHttpRequest因为考虑到安全性不允许发送请求到外部服务器,只好寻找其它的办法。(见2.2。)
说到AJAX首先要思考的两个问题,第一个是AJAX以何种格式来交换数据?第二个是跨域的需求如何解决?这两个问题目前都有不同的解决方案,比如数据可以用自定义字符串或者用XML来描述,跨域可以通过服务器端代理来解决。但到目前为止最被推崇或者说首选的方案还是用JSON来传数据,靠JSONP来跨域。
JSON(JavaScript Object Notation)和JSONP(JSON with Padding)虽然只有一个字母的差别,但其实他们根本不是一回事儿:JSON是一种数据交换格式,而JSONP是一种依靠开发人员的聪明才智创造出的一种非官方跨域数据交互协议。我们拿最近比较火的谍战片来打个比方,JSON是地下党们用来书写和交换情报的“暗号”,而JSONP则是把用暗号书写的情报传递给自己同志时使用的接头方式。看到没?一个是描述信息的格式,一个是信息传递双方约定的方法。
2.1 JSON
JSON:javaScript对象表示法(JavaScript Object Notation)
JSON is a subset of the object literal notation of JavaScript. Since JSON is a subset of JavaScript, it can be used in the language with no muss or fuss.
JSON是一种轻量级的数据交换格式。(json.org)
JSON是存储和交换文本信息的语法,类似XML。它采用键值对的方式来组织,易于人们阅读和编写,同时也易于机器解析和生成。JSON是独立于语言的,也就是说不管什么语言,都可以解析json,只需要按照json的规则来就行。
JSON语法规则:JSON能够以非常简单的方式来描述数据结构,XML能做的它都能做,因此在跨平台方面两者完全不分伯仲。
(1)JSON只有两种数据类型描述符,大括号{}和方括号[],其余英文冒号:是映射符,英文逗号,是分隔符,英文双引号""是定义符。
(2)大括号{}用来描述一组“不同类型的无序键值对集合”(每个键值对可以理解为OOP的属性描述),方括号[]用来描述一组“相同类型的有序数据集合”(可对应OOP的数组)。
(3)上述两种集合中若有多个子项,则通过英文逗号,进行分隔。
(4)键值对以英文冒号:进行分隔,并且建议键名都加上英文双引号"",以便于不同语言的解析。
(5)JSON内部常用数据类型有字符串、数字、布尔、日期、null 等,字符串必须用双引号引起来,其余的都不用,日期类型比较特殊,建议如果客户端没有按日期排序功能需求的话,那么把日期时间直接作为字符串传递就好,可以省去很多麻烦。
json解析的方法有两种:eval()和parse()方法。————建议尽量使用JSON.parse方法来解析json里的字符串。
JSON和XML比较:
(1)JSON的长度和XML格式比起来很短小;
(2)JSON读写的速度更快;
(3)JSON可以使用JavaScript内建的方法直接进行解析,转换成JavaScipt对象,非常方便
JSON的优点:
(1)基于纯文本,跨平台传递极其简单;
(2)Javascript原生支持,后台语言几乎全部支持;
(3)轻量级数据格式,占用字符数量极少,特别适合互联网传递;
(4)可读性强,容易编写和解析;
JSON的缺点:
(1)JSON在服务端语言的支持不像XML那么广泛,不过JSON.org上提供很多语言的库。
(2)如果你使用eval()来解析的话,会容易出现安全问题。
尽管如此,JSON的优点还是很明显的。他是Ajax数据交互的很理想的数据格式。
2.2 JSONP
JSONP:JSON with Padding(填充式 JSON 或参数式 JSON),JSONP是一个非官方的协议,它允许在服务器端集成Script tags返回至客户端,通过javascript callback的形式实现跨域访问(这仅仅是JSONP简单的实现形式)。
1、一个众所周知的问题,由于同源策略,Ajax直接请求普通文件存在跨域无权限访问的问题,甭管你是静态页面、动态网页、web服务、WCF,只要是跨域请求,一律不准;
2、不过我们又发现,Web页面上调用js文件时则不受是否跨域的影响(不仅如此,我们还发现凡是拥有”src”这个属性的标签都拥有跨域的能力,比如<script>、<img>、<iframe>);
3、于是可以判断,当前阶段如果想通过纯web端(ActiveX控件、服务端代理、属于未来的HTML5之Websocket等方式不算)跨域访问数据就只有一种可能,那就是在远程服务器上设法把数据装进js格式的文件里,供客户端调用和进一步处理;
4、恰巧我们已经知道有一种叫做JSON的纯字符数据格式可以简洁的描述复杂数据,更妙的是JSON还被js原生支持,所以在客户端几乎可以随心所欲的处理这种格式的数据;
5、这样子解决方案就呼之欲出了,web客户端通过与调用脚本一模一样的方式,来调用跨域服务器上动态生成的js格式文件(一般以JSON为后缀),显而易见,服务器之所以要动态生成JSON文件,目的就在于把客户端需要的数据装入进去。
6、客户端在对JSON文件调用成功之后,也就获得了自己所需的数据,剩下的就是按照自己需求进行处理和展现了,这种获取远程数据的方式看起来非常像AJAX,但其实并不一样。
7、为了便于客户端使用数据,逐渐形成了一种非正式传输协议,人们把它称作JSONP,该协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。
一句话总结:由于同源策略的限制,XmlHttpRequest只允许请求当前源(域名、协议、端口)的资源,为了实现跨域请求,可以通过script标签实现跨域请求,然后在服务端输出JSON数据并执行回调函数,从而解决了跨域的数据请求。
以下两篇文章以浅显循序渐进的方式对jsonp的概念进行了阐释,对理解其来龙去脉很有帮助:
2.2.2 JSONP的实现
JSONP借助了script标签节点跨域访问/获取的特性。JSONP实现跨域请求的原理简单的说,就是动态创建<script>
标签,然后利用<script>
的src 不受同源策略约束来跨域获取数据。
JSONP由两部分组成:回调函数和数据。回调函数是当响应到来时应该在页面中调用的函数。回调函数的名字一般是在请求中指定的。而数据就是传入回调函数中的 JSON 数据。
a域名去声明一个方法,b域名去调用这个方法,通过script标签可以向不同域名提交http请求。
一、原生js实现jsonp
1.最简单的一种,客户端(a域名)的html文件
<!doctype html> <html lang="en"> <head></head> <body> <script type="text/javascript"> function jsonpCallback(result) { for(var i in result) { console.log(i+":"+result[i]);//循环输出result的元素 } } </script> <script type="text/javascript" src="http://180.167.10.100/update/index.php?callback=jsonpCallback"></script> <!-- 传递固定参数的方式 --> <!-- <script type="text/javascript" src="http://180.167.10.100/update/index.php?os=Windows&version=3.2.3.660&callback=jsonpCallback"></script> --> </body> </html>
或者
<!doctype html> <html lang="en"> <head></head> <body> <script type="text/javascript"> function jsonpCallback(result) { //alert(result); for(var i in result) { console.log(i+":"+result[i]);//循环输出result的元素 } } var JSONP=document.createElement("script"); JSONP.type="text/javascript"; JSONP.src="http://180.167.10.100/update/index.php?callback=jsonpCallback"; document.getElementsByTagName("head")[0].appendChild(JSONP); /*传递参数的方式*/ /*JSONP.src="http://180.167.10.100/update/index.php?os=Windows&version=3.2.3.660&callback=jsonpCallback";*/ </script> </body> </html>
服务端(b域名)的php代码:
<?php //服务端返回JSON数据 //$arr=array('a'=>1,'b'=>2,'c'=>3); $arr->IsLatestVersion = False; $arr->version = ‘3.3.2.764’; $arr->url = "http://172.30.28.18/update/releases/3.2.1.601/Windows/cdos-browser2_3.2.1.601.exe"; $result=json_encode($arr); //动态执行回调函数 $callback=$_GET['callback']; echo $callback."($result)"; ?>
2.在实际的项目中,我们的jsonp一般不会直接在html文件中实现,因为向服务器获取数据的时机需要根据我们项目的实际需求来决定,所以一般需要动态的在我们所开发的模块或某个js文件中创建script标签以及传递相关参数(此参数也可能是项目过程中动态生成的),这时我们可以在客户端(a域名)的js文件中如下实现:
// 得到查询结果后的回调函数 function jsonpCallback(result) { for(var i in result) { console.log(i+":"+result[i]);//循环输出result的元素 } } function getVersionInfo(){ var os = "Windows"; var version = "3.2.3.660"; var head = document.getElementsByTagName('head')[0]; var script = document.createElement('script');//创建script标签,设置其属性 var url = "http://180.167.10.100/update/index.php?os="+os+"&version="+version+"&callback=jsonpCallback"; script.type = "text/javascript"; script.setAttribute('src', url);//script.src= url;//提供jsonp服务的url地址 head.appendChild(script);// 把script标签加入head,此时调用开始 } getVersionInfo(); //具体根据实际情况在合适位置调用即可
但是此时很可能出现报错“jsonpCallback未定义”,原因其实很简单,作为jsonp的回调函数,jsonpCallback必须是全局函数,而一般由于项目的模块化和封装我们的函数都是局部函数,此时我们必须将其全局化,可以将此回调函数jsonpCallback单拿出来放在html文件中,或者将其通过如下方法绑定到window对象上实现全局化:
(function(){ // 得到查询结果后的回调函数 window['jsonpCallback'] = function(data){ for(var i in result) { console.log(i+":"+result[i]);//循环输出result的元素 } }; function getVersionInfo(){ var os = "Windows"; var version = "3.2.3.660"; var head = document.getElementsByTagName('head')[0]; var script = document.createElement('script');//创建script标签,设置其属性 var url = "http://180.167.10.100/update/index.php?os="+os+"&version="+version+"&callback=jsonpCallback"; script.type = "text/javascript"; script.setAttribute('src', url);//script.src= url;//提供jsonp服务的url地址 head.appendChild(script);// 把script标签加入head,此时调用开始 } getVersionInfo();//具体根据实际情况在合适位置调用即可 })();
这样jsonp的原理就很清楚了,首先在客户端注册一个callback(名字任意),然后动态创建script标签通过src引入服务器端的php文件(类似引入js文件的方式),同时将客户端注册的callback的名字传给服务器,php文件载入成功后,服务器先生成我们需要的 json 数据,然后将其作为参数传入我们在url参数中指定的函数,所以jsonp是需要服务器端的页面进行相应的配合的。
二、jQuery实现jsonp
jQuery本身就支持JSONP,jQuery封装的$.ajax
中有一个dataType
属性,将该属性设置成dataType:"jsonp"
,就能指定按照jsonp方式访问远程服务从而实现JSONP跨域了。
注意:虽然jQuery将JSONP封装在$.ajax
中,但是其本质与$.ajax
不一样。如果设为dataType: 'jsonp',这个$.ajax方法就和ajax XmlHttpRequest没什么关系了,取而代之的则是JSONP协议。
注:jsonp的方式只是针对get请求方式,不支持post请求。这也是Jsonp方式的局限性。
实现原理与用原生js是一样的,只不过我们不需要手动的插入script标签以及定义回调函数,jquery在处理jsonp类型的ajax时(虽然jquery也把jsonp归入了ajax,但其实不是一回事儿),会自动帮我们生成回调函数并把数据取出来供success属性方法来调用。
而如果我们想指定自己的回调函数,或者说服务上规定了固定回调函数名怎么办呢?只要如下所示添加一个jsonpCallback的选项,就可以指定我们自己的回调方法名myCallback,远程服务接受callback参数的值就不再是自动生成的回调名,而是myCallback。
jsonp及jsonpCallback的解释:
更多参数请参考:jQuery.ajax()
客户端(a域名)的js代码实现如下:
$(document).ready(function(){ $.ajax({ url: "http://180.167.10.100/update/index.php", /*url: "http://180.167.10.100/update/index.php?os=Windows&version=3.2.3.660",*//*也可以直接在url里面传递数据*/ type: "GET", dataType: "jsonp",
jsonp: "callback",//用于指示后台(php)获取数据
jsonpCallback: "myCallback",//用于添加自己的回调函数,无此项则回调函数默认为success async: false, data: { 'version': "3.2.3.660", 'os': "Windows", }, timeout:3000, success: function(result) { console.log(result); for(var i in result) { console.log(i+":"+result[i]);//循环输出result的元素 } }, error: function(xhr) { console.log(xhr); }, }); });
服务器端(b域名)的php代码实现如下:
<?php header('Content-Type: text/html; charset=utf-8'); /*获取客户端的数据*/ $callback=$_GET['callback'];/*注意跟客户端指定的jsonp的值一致*/ $os = $_GET['os']; $version = $_GET['version']; /*对数据的操作--省略*/ /*对数据的操作--省略*/ /*将数据返回给客户端*/ $response->IsLatestVersion = False; $response->version = '3.3.2.764'; $response->url = 'http://180.167.10.100/update/releases/3.3.2.764/Windows/cdos-browser2_update_3.3.2.764.exe'; $responseJSON = json_encode($response); echo $callback."($responseJSON)"; ?>
三、$.getJSON方法实现jsonp
$.getJSON()是专门为ajax获取json数据而设置的,并且支持"跨域"调用,其语法的格式为: getJSON(url,[data],[callback])
实现原理与用原生js是一样的,同样我们不需要手动的插入script标签以及定义回调函数,jquery会自动生成一个全局函数来替换callback=?中的问号,之后获取到数据后又会自动销毁,实际上就是起一个临时代理函数的作用。$.getJSON方法会自动判断是否跨域,不跨域的话,就调用普通的ajax方法;跨域的话,则会以异步加载js文件的形式来调用jsonp的回调函数。
客户端代码实现如下:
//JQuery JSONP Support var url = "http://www.mydomain.com/api/suggest.php?symbol=IBM&callback=?"; $.getJSON(url, function(result){ for(var i in result) { console.log(i+":"+result[i]);//循环输出result的元素 } });
1、ajax和jsonp这两种技术在调用方式上“看起来”很像,目的也一样,都是请求一个url,然后把服务器返回的数据进行处理,因此jquery和ext等框架都把jsonp作为ajax的一种形式进行了封装;
2、但ajax和jsonp其实本质上是不同的东西。ajax的核心是通过XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加<script>标签来调用服务器提供的js脚本。
3、所以说,其实ajax与jsonp的区别不在于是否跨域,ajax通过服务端代理一样可以实现跨域,jsonp本身也不排斥同域的数据的获取。
4、还有就是,jsonp是一种方式或者说非强制性协议,如同ajax一样,它也不一定非要用json格式来传递数据,如果你愿意,字符串都行,只不过这样不利于用jsonp提供公开服务。
总而言之,jsonp不是ajax的一个特例,哪怕jquery等巨头把jsonp封装进了ajax,也不能改变这一点!
2.2.4 $.get、$.post、$getJSON、$ajax的用法跟区别
首先,$.ajax()是jquery中通用的一个ajax封装,$.get()和$.post()其实都是$.ajax()的一种,在$.ajax()中有一个type属性,专门用来指定是get请求还是post请求的。
$.getJSON()是专门为ajax从服务器获取json数据而设置的,并且支持"跨域"调用。jQuery的getJSON()函数,只是设置了JSON参数的ajax()函数的一个简化版本。这个函数也是可以跨域使用的,相比get()、post()有一定优势。(JSON是一种理想的数据传输格式,它能够很好的融合与JavaScript或其他宿主语言,并且可以被JS直接使用。使用JSON相比传统的通过 GET、POST直接发送“裸体”数据,在结构上更为合理,也更为安全。)
注意:$.getJSON()数据最终还是通过get方式发送数据出去的,这就决定了,发送的data数据量不能太多,否则造成url太长接收失败(getJSON方式是不可能有post方式递交的)。
具体区别如下:
1.$.ajax()
$.ajax()是jquery中通用的一个ajax封装,其语法的格式为:
$.ajax(options)
其中options是一个object类型,它指明了本次ajax调用的具体参数,这里我把最常用的几个参数附上
$.ajax({ url: 'submit.aspx', datatype: "json", type: 'post', success: function (e) { //成功后回调 alert("回调函数成功了"); }, error: function(e){ //失败后回调 alert("服务器请求失败"); }, beforeSend: function(){ //发送请求前调用,可以放一些"正在加载"之类额话 alert("正在加载"); } })
2.$.get
$.get()方法使用GET方式来进行异步请求,它的语法结构为:
$.get( url [, data] [, callback] )
解释一下这个函数的各个参数:
url:string类型,ajax请求的地址。
data:可选参数,object类型,发送至服务器的key/value数据会作为QueryString附加到请求URL中。
callback:可选参数,function类型,当ajax返回成功时自动调用该函数。
$.get("submit.php",{id:'123',name:'小王',},function(data,state){ //这里显示从服务器返回的数据 alert(data); //这里显示返回的状态 if(state == 'ok'){ alert("返回数据成功"); }else{ alert("返回数据失败"); } });
3.$.post()
$.post()方法使用POST方式来进行异步请求,它的语法结构为:
$.post(url,[data],[callback],[type])
这个方法和$.get()用法差不多,唯独多了一个type参数,那么这里就只介绍type参数吧,其他的参考上面$.get()的。
type:type为请求的数据类型,可以是html,xml,json等类型,如果我们设置这个参数为:json,那么返回的格式则是json格式的,如果没有设置,就和$.get()返回的格式一样,都是字符串的。
$.post("submit.php",{id:'123',name:'小明',},function(data,state){ //这里显示从服务器返回的数据 alert(data); //这里显示返回的状态 if(state == 'ok'){ alert("返回数据成功"); }else{ alert("返回数据失败"); }, "json");
4.$.getJSON()
$.getJSON()是专门为ajax获取json数据而设置的,并且支持"跨域"调用,其语法的格式为:
getJSON(url,[data],[callback])
url:string类型, 发送请求地址
data :可选参数, 待发送 Key/value 参数 ,同get,post类型的data
callback :可选参数,载入成功时回调函数,同get,post类型的callback
2.2.4 JSONP的优缺点
JSONP使用起来方便,是目前比较流行的跨域方式,它的优缺点如下:
JSONP的优点:
(1)它不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制,JSONP可以跨越同源策略;
(2)它的兼容性更好,在更加古老的浏览器中都可以运行,不需要XMLHttpRequest或ActiveX的支持
(3)在请求完毕后可以通过调用callback的方式回传结果。将回调方法的权限给了调用方。这个就相当于将controller层和view层终于分开了。我提供的jsonp服务只提供纯服务的数据,至于提供服务以
后的页面渲染和后续view操作都由调用者来自己定义就好了。如果有两个页面需要渲染同一份数据,你们只需要有不同的渲染逻辑就可以了,逻辑都可以使用同
一个jsonp服务。
JSONP的缺点:适用范围太小(只能GET), 有安全风险(返回的代码会直接执行)
(1)它只支持GET请求而不支持POST等其它类型的HTTP请求
(2)它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题。
(3)jsonp在调用失败的时候不会返回各种HTTP状态码,要确定 JSONP 请求是否失败并不容易。虽然 HTML5 给<script>
元素新增了一个 onerror事件处理程序,但目前还没有得到任何浏览器支持。为此,开发人员不得不使用计时器检测指定时间内是否接收到了响应。
(4)存在安全风险。因为JSONP 是从其他域(服务端)中加载代码并直接执行,如果其他域不安全,很可能会在响应中夹带一些恶意代码,于是所有调用这个 jsonp的网站都会存在漏洞,无法把危险控制在一个域名下,而此时除了完全放弃 JSONP 调用之外,没有办法追究。因此在使用不是你自己运维的 Web 服务时,一定得保证它安全可靠。
3、解决跨域问题-CORS/XHR2
3.1 概念
为了改善网络应用程序,开发人员要求浏览器供应商允许跨域请求。
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-Origin Resource Sharing),它允许浏览器向跨源服务器,发出XMLHttpRequest
请求,从而克服了AJAX只能同源使用的限制。
跨域请求主要用于:
- 调用XMLHttpRequest或fetchAPI通过跨站点方式访问资源
- 网络字体,例如Bootstrap(通过CSS使用@font-face 跨域调用字体)
- 通过canvas标签,绘制图表和视频。
HTML5提供的XMLHttpRequest Level2已经实现了跨域访问以及其他的一些新功能,CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。
整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
3.2 实现
CORS背后的基本思想是使用自定义的HTTP头部允许浏览器和服务器相互了解对方,从而决定请求或响应成功与否.
Access-Control-Allow-Origin:指定授权访问的域
Access-Control-Allow-Methods:授权请求的方法(GET, POST, PUT, DELETE,OPTIONS等)
如在服务器端添加如下代码即可:
header('Access-Control-Allow-Origin:*');//设置可以访问的域,星号代表所有域都可以访问
header('Access-Control-Allow-Methods:POST,GET');
配合XHR2的IE10以下跨域:
在代码中js加上一句jQuery.support.cors =true;或者$.support.cors =true;
然后:设置IE浏览器->Internet选项->安全->自定义级别->其他选项下面的->通过源数据:选择“启用”或者“提示”
详细可参考:
3.3 CORS 对比 JSONP
都能解决 Ajax直接请求普通文件存在跨域无权限访问的问题
(1)JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求
(2)使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理
(3)JSONP主要被老的浏览器支持,它们往往不支持CORS,而绝大多数现代浏览器都已经支持了CORS!
4、解决跨域问题-代理
通过在同域名的web服务器端创建一个代理,用本地服务器的后台调用其他域的后台服务,然后把相应结果返回给前端,这样前端调用同域名服务器的服务和其他域的服务一样了。(由后台实现,了解)
5、其他跨域方法
参考: