在javascript中的跨域解决

跨域产生的原因

跨域是由浏览器的同源策略引起的,即不同源(协议,域名,端口中其中有一个不同)的js是不能读取对方的资源的。当要网站中的js要请求其他网站的数据时就会产生跨域问题,就像下面这样,浏览器会报错。

 

关于会产生跨域的情况,直接在网上找了一张图,做了很详细的总结。

 

 

当然这都不是重点,重要的是如何来解决跨域问题。

跨域解决方案

1.jsonp

可跨域的html标签

所有具有src属性的HTML标签都是可以跨域的,包括<script><img><iframe>,所以我们通常会把一些图片资源放到第三方服务器上,然后可以通过<img>标签的src属性引用。例如:

var img = new Image()
img.src = 'http://some/picture'      // 发送http请求

利用<script>标签实现跨域

  首先我们来说一下什么是jsonp,我们都知道json是一种数据交换格式,虽然json与jsonp只差了一个字母但是它们两个却完全是两码事。jsonp是依靠开发人员的聪明才智创造出的一种非官方的跨域数据交换协议。它允许在服务器端集成Script tags返回至客户端。
  但是这和我们想要跨域请求数据又有什么关系呢?虽然我们不能直接发送ajax请求数据,但是要知道<srcitp>标签是可以跨域的,如果我们动态的创建一个<script>标签,同时src到不同源的服务端url,服务端按照约定返回一段可执行js的代码。类似这样:

callbackFunction([“customername1","customername2"])

而在客户端我们只需要定义一个预定好的回调函数即可。

var callbackFunction = function(data){
    // 处理跨域请求得到的数据
};
var script = $('<script>', {src: 'http://b.a.com/bar'})
$('body').append(script)

其中的callbackFuncton是我们在客户端定义好的在数据请求成功后要执行的回调函数。
好了总结一下用jsonp请求数据的基本流程。

  1. 首先在客户端注册一个callback, 然后把callback的名字传给服务器。

  2. 服务器先生成 json 数据。

  3. 然后以 javascript 语法的方式,生成一个function , function 名字就是传递上来的参数 jsonp.

  4. 将 json 数据直接以入参的方式,放置到 function 中,这样就生成了一段 js 语法的文档,返回给客户端。

  5. 客户端浏览器,解析script标签,并执行返回的 javascript 文档,此时数据作为参数,传入到了客户端预先定义好的 callback 函数里.(动态执行回调函数)

jsonp的缺点

    1. 没有关于 JSONP 调用的错误处理。如果动态脚本插入有效,就执行调用;如果无效,就静默失败。失败是没有任何提示的。例如,不能从服务器捕捉到 404 错误,也不能取消或重新开始请求.

    2. JSONP 的另一个主要缺陷是被不信任的服务使用时会很危险。因为 JSONP 服务返回打包在函数调用中的 JSON 响应,而函数调用是由浏览器执行的,这使宿主 Web 应用程序更容易受到各类攻击。

跨域资源共享(CORS)

CORS(Cross-Origin Resource Sharing)跨域资源共享,定义了必须在访问跨域资源时,浏览器与服务器应该如何沟通。CORS背后的基本思想就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是失败。

<script type="text/javascript">
    var xhr = new XMLHttpRequest();
    xhr.open("GET", "/trigkit4",true);
    xhr.send();
</script>

以上的trigkit4是相对路径,如果我们要使用CORS,相关Ajax代码可能如下所示:

<script type="text/javascript">
    var xhr = new XMLHttpRequest();
    xhr.open("GET", "http://segmentfault.com/u/trigkit4/",true);
    xhr.send();
</script>

代码与之前的区别就在于相对路径换成了其他域的绝对路径,也就是你要跨域访问的接口地址。

服务器端对于CORS的支持,主要就是通过设置Access-Control-Allow-Origin来进行的。如果浏览器检测到相应的设置,就可以允许Ajax进行跨域的访问。

 

CORS和JSONP对比

CORS与JSONP相比,无疑更为先进、方便和可靠。

    1、 JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求。

    2、 使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理。

    3、 JSONP主要被老的浏览器支持,它们往往不支持CORS,而绝大多数现代浏览器都已经支持了CORS)。

通过修改document.domain来跨子域

浏览器都有一个同源策略,其限制之一就是第一种方法中我们说的不能通过ajax的方法去请求不同源中的文档。 它的第二个限制是浏览器中不同域的框架之间是不能进行js的交互操作的。
不同的框架之间是可以获取window对象的,但却无法获取相应的属性和方法。比如,有一个页面,它的地址是http://www.example.com/a.html , 在这个页面里面有一个iframe,它的src是http://example.com/b.html, 很显然,这个页面与它里面的iframe框架是不同域的,所以我们是无法通过在页面中书写js代码来获取iframe中的东西的:

<script type="text/javascript">
    function test(){
        var iframe = document.getElementById('ifame');
        var win = document.contentWindow;//可以获取到iframe里的window对象,但该window对象的属性和方法几乎是不可用的
        var doc = win.document;//这里获取不到iframe里的document对象
        var name = win.name;//这里同样获取不到window对象的name属性
    }
</script>
<iframe id = "iframe" src="http://example.com/b.html" onload = "test()"></iframe>

这个时候,document.domain就可以派上用场了,我们只要把http://www.example.com/a.html 和 http://example.com/b.html这两个页面的document.domain都设成相同的域名就可以了。但要注意的是,document.domain的设置是有限制的,我们只能把document.domain设置成自身或更高一级的父域,且主域必须相同。

1.在页面 http://www.example.com/a.html 中设置document.domain:

<iframe id = "iframe" src="http://example.com/b.html" onload = "test()"></iframe>
<script type="text/javascript">
    document.domain = 'example.com';//设置成主域
    function test(){
        alert(document.getElementById('iframe').contentWindow);//contentWindow 可取得子窗口的 window 对象
    }
</script>


2.在页面 http://example.com/b.html 中也设置document.domain:

<script type="text/javascript">
    document.domain = 'example.com';//在iframe载入这个页面也设置document.domain,使之与主页面的document.domain相同
</script>

修改document.domain的方法只适用于不同子域的框架间的交互。

使用window.name来进行跨域

window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的

使用HTML5的window.postMessage方法跨域

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

 

举栗子:1.CORS解决跨域

CORS是一个W3C标准,全称是”跨域资源共享”(Cross-origin resource sharing)。 对于这个方式,阮一峰老师总结的文章特别好,希望深入了解的可以看一下

这里我就简单的说一说大体流程。

  1. 对于客户端,我们还是正常使用xhr对象发送ajax请求。
    唯一需要注意的是,我们需要设置我们的xhr属性withCredentials为true,不然的话,cookie是带不过去的哦,设置: xhr.withCredentials = true;
  2. 对于服务器端,需要在 response header中设置如下两个字段:
    Access-Control-Allow-Origin:
    Access-Control-Allow-Credentials:true
    这样,我们就可以跨域请求接口了。

举栗子:2.JSONP解决跨域

// 定义一个fun函数
function fun(fata) {
    console.log(data);
};
// 创建一个脚本,并且告诉后端回调函数名叫fun
var body = document.getElementsByTagName('body')[0];
var script = document.gerElement('script');
script.type = 'text/javasctipt';
script.src = 'demo.js?callback=fun';
body.appendChild(script);

返回的js脚本,直接会执行。所以就执行了事先定义好的fun函数了,并且把数据传入了进来。

fun({"name": "name"})

当然,这个只是一个原理演示,实际情况下,我们需要动态创建这个fun函数,并且在数据返回的时候销毁它。

因为在实际使用的时候,我们用的各种ajax库,基本都包含了jsonp的封装,不过我们还是要知道一下原理,不然就不知道为什么jsonp不能发post请求了~



 

举栗子:3.服务器代理

  浏览器有跨域限制,但是服务器不存在跨域问题,所以可以由服务器请求所要域的资源再返回给客户端。

 

服务器代理是万能的。
  
作者:黄家兴
链接:https://www.zhihu.com/question/26376773/answer/244453931
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

举栗子:4.document.domain来跨子域

对于主域名相同,而子域名不同的情况,可以使用document.domain来跨域 这种方式非常适用于iframe跨域的情况,直接看例子吧 比如a页面地址为 b页面为 。 这样就可以通过分别给两个页面设置 document.domain = 来实现跨域。 之后,就可以通过 parent 或者 window[‘iframename’]等方式去拿到iframe的window对象了。

 

使用window.name进行跨域

window.name跨域同样是受到同源策略限制,父框架和子框架的src必须指向统一域名。window.name的优势在于,name的值在不同的页面(或者不同的域名),加载后仍然存在,除非你显示的更改。并且支持的长度达到2M.

//a页面的代码
<script type="text/javascript">
   iframe = document.createElement('iframe');
   iframe.style.display = 'none';
var state = 0;

   iframe.onload = function() {
if(state === 1) {
var data = iframe.contentWindow.name;
         console.log(data);
         iframe.contentWindow.document.write('');
         iframe.contentWindow.close();
         document.body.removeChild(iframe);
     } else if(state === 0) {
         state = 1;
         iframe.contentWindow.location = 
         'http://m.zhuanzhuan.58.com:8887/b.html';
     }
   };
   document.body.appendChild(iframe);
</script>

 

//b页面代码
<script type="text/javascript">
   window.name = "hello";
</script>

作者:黄家兴
链接:https://www.zhihu.com/question/26376773/answer/244453931
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

document.domain来跨子域

对于主域名相同,而子域名不同的情况,可以使用document.domain来跨域 这种方式非常适用于iframe跨域的情况,直接看例子吧 比如a页面地址为 b页面为 。 这样就可以通过分别给两个页面设置 document.domain = 来实现跨域。 之后,就可以通过 parent 或者 window[‘iframename’]等方式去拿到iframe的window对象了。

 

举栗子:5.使用window.name进行跨域

window.name跨域同样是受到同源策略限制,父框架和子框架的src必须指向统一域名。window.name的优势在于,name的值在不同的页面(或者不同的域名),加载后仍然存在,除非你显示的更改。并且支持的长度达到2M.

//a页面的代码
<script type="text/javascript">
   iframe = document.createElement('iframe');
   iframe.style.display = 'none';
var state = 0;

   iframe.onload = function() {
if(state === 1) {
var data = iframe.contentWindow.name;
         console.log(data);
         iframe.contentWindow.document.write('');
         iframe.contentWindow.close();
         document.body.removeChild(iframe);
     } else if(state === 0) {
         state = 1;
         iframe.contentWindow.location = 
         'http://m.zhuanzhuan.58.com:8887/b.html';
     }
   };
   document.body.appendChild(iframe);
</script>

 

//b页面代码
<script type="text/javascript">
   window.name = "hello";
</script>

举栗子:6.使用postMessage实现页面之间通信

信息传递除了客户端与服务器之前的传递,还存在以下几个问题:

  • 页面和新开的窗口的数据交互。
  • 多窗口之间的数据交互。
  • 页面与所嵌套的iframe之间的信息传递。

window.postMessage是一个HTML5的api,允许两个窗口之间进行跨域发送消息。这个应该就是以后解决dom跨域通用方法了,具体可以参照MDN。

补充:window.name和location.hash。很适用于iframe的跨域,不过iframe用的比较少了,所以这些方法也就有点过时了。

这些就是我对跨域的了解了,实际情况下,一般用cors,jsonp等常见方法就可以了。不过遇到了一些非常规情况,我们还是需要知道有更多的方法可以选择的





以上内容来自网络,若有侵权,告知必删

posted @ 2019-03-20 19:42  拾荒哲  阅读(400)  评论(0编辑  收藏  举报