解决跨域问题

栏目: Json · 发布时间: 5年前

内容简介:AJAX之所以需要“跨域”,就是由于浏览器的同源策略。即一个页面的AJAX只能获取这个页面相同源或者相同域的数据。 如何叫“同源”或者“同域”呢?——协议、域名、端口号都必须相同。当跨域请求时,一般都会看到这个错误:JSONP(JSON with Padding) 是一种跨域请求方式。主要原理是利用了script 标签可以跨域请求的特性,由其 src 属性发送请求到服务器,服务器返回 JavaScript 代码,浏览器接受响应,然后直接执行,这和通过 script 标签引用外部文件的原理是一样的。

AJAX之所以需要“跨域”,就是由于浏览器的同源策略。即一个页面的AJAX只能获取这个页面相同源或者相同域的数据。 如何叫“同源”或者“同域”呢?——协议、域名、端口号都必须相同。

当跨域请求时,一般都会看到这个错误:

XMLHttpRequest cannot load http://xxxxx.com. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access.

JSONP

JSONP(JSON with Padding) 是一种跨域请求方式。主要原理是利用了script 标签可以跨域请求的特性,由其 src 属性发送请求到服务器,服务器返回 JavaScript 代码,浏览器接受响应,然后直接执行,这和通过 script 标签引用外部文件的原理是一样的。

JSONP由两部分组成: 回调函数和数据 ,回调函数一般是在浏览器控制,作为参数发往服务器端(当然,你也可以固定回调函数的名字,但客户端和服务器端的名称一定要一致)。当服务器响应时,服务器端就会把该函数和数据拼成字符串返回。

JSONP的请求过程:

http://example.com/api/?callback=jsonpCallback
// 包装在一个立即调用的函数表达式
(function () {
  var _callbacks = 0
// 给回调函数添加唯一标知的 id
  window.jsonp = function (url, callback) {
    var id = 'jsonp_cb_' + _callbacks,
        existing = document.scripts[0],
// 创建script标签并加入到页面中
        script = document.createElement('script')

    script.src = url + (~url.indexOf('?') ? '&' : '?') + 'callback=' + id
    existing.parentNode.insertBefore(script, existing)

//创建jsonp回调函数
    window[id] = function (data) {
// 因为 script 标签的 src 属性只在第一次设置的时候起作用,导致 script 标签没法重用,所以每次完成操作之后要移除
      script.parentNode.removeChild(script)
      callback(data)
      delete window[id]
    }

    _callbacks += 1
  }
}())

这段代码将一个jsonp函数添加到window,windo接受要发送请求的URL,一旦此请求完成后回复exectue。 指定的回调附加到window,因此请求的脚本在查找和调用它时不会出现问题。 回调完成后,外部资源和 window 新附加的回调函数都将被删除。

JSONP 使用简单且兼容性不错,但是只限于 get 请求。

当然,这一段原生的 JSONP 如果要追求完整,还可以添加处理成功或失败之后的操作以及处理超时等 error 的操作。

CORS (Cross-Origin Resource Sharing)

跨源资源共享(CORS)是一种允许来自浏览器的跨域通信的W3C规范,允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。

CORS的用例很简单。 比如网站alice.com有一些网站bob.com想要访问的数据。 传统上,在浏览器的相同原始策略下不允许这种类型的请求。 但是,通过支持CORS请求,alice.com可以添加一些允许bob.com访问数据的特殊响应标头。

从这个例子可以看出,CORS支持需要服务器和客户端之间的协调。

使用 CORS 跨域的时候和普通的 ajax 过程是一样的,只是浏览器在发现这是一个跨域请求的时候会自动帮我们处理一些事情,所以说 只要服务端提供支持,前端是不需要做额外的事情的

Chrome,Firefox,Opera和Safari都使用 XMLHttpRequest2 对象。 Internet Explorer使用类似的 XDomainRequest 对象,该对象的工作方式与 XMLHttpRequest 对应方式大致相同,但添加了额外的安全预防措施。

document.domain

获取/设置当前文档的原始域部分, 用于 同源策略.

var domainString = document.domain;
document.domain = string;

可以给document.domain赋值,但只能赋成当前的域名或基础域名。

javascript:alert(document.domain = "abc.net"); //jb51.net
javascript:alert(document.domain = "www.abc.net");

上面的赋值都是成功的,因为 www.abc.net 是当前的域名,而 abc.net 是基础域名。

但是下面的赋值就会出来”参数无效”的错误:

javascript:alert(document.domain = “cde.net”); //参数无效
javascript:alert(document.domain = "www.123.net"); //参数无效

因为 cde.netwww.123.net 不是当前的域名也不是当前域名的基础域名,所以会有错误出现。这是为了防止有人恶意修改document.domain来实现跨域偷取数据。

利用document.domain 实现跨域:

前提条件:这两个域名必须属于同一个基础域名,而且所用的协议,端口都要一致,也就是二级域名都相同,否则无法利用document.domain进行跨域。

比如在:aaa.com的一个网页(a.html)里面 利用iframe引入了一个bbb.com里的一个网页(b.html)。

这时在a.html里面可以看到b.html里的内容,但是却不能利用javascript来操作它。因为这两个页面属于不同的域,在操作之前,浏览器会检测两个页面的域是否相等,如果相等,就允许其操作,如果不相等,就会拒绝操作。

这里不可能把a.html与b.html利用JS改成同一个域的。因为它们的基础域名不相等。(强制用JS将它们改成相等的域的话会报跟上面一样的”参数无效错误。”)

所以如果在a.html里引入aaa.com里的另一个网页,是不会有这个问题的,因为域相等。

有另一种情况,两个子域名: a.xxx.comb.xxx.com
a里的一个网页(a.html)引入了 b 里的一个网页(b.html),这时a.html里同样是不能操作 b.html 里面的内容的。

因为document.domain不一样,一个是aaa.xxx.com,另一个是bbb.xxx.com。

这时我们就可以通过将两个页面的domain改成一样的,在a.html里与b.html里都加入: document.domain = "xxx.com"; 这样这两个页面就可以互相操作了。也就是实现了同一基础域名之间的”跨域”。

postMessage

postMessage()方法允许来自不同源的脚本采用 异步方式 进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递。

在需要发送消息的源窗口调用postMessage方法即可发送消息。

源窗口

源窗口可以是全局的window对象,也可以是以下类型的窗口:

  • 文档窗口中的iframe:
  • var iframe = document.getElementById('my-iframe');
    var win = iframe.documentWindow;
    
  • JavaScript打开的弹窗: var win = window.open();

  • 当前文档窗口的父窗口: var win = window.parent;
  • 打开当前文档的窗口: var win = window.opener();

找到源window对象后,即可调用postMessage API向目标窗口发送消息:

语法

win.postMessage('Hello', 'http://baidu.com/');

postMessage函数接收两个参数:

  1. data:要传递的数据,html5 规范中提到该参数可以是JavaScript的任意基本类型或可复制的对象,然而并不是所有浏览器都做到了这点儿,部分浏览器只能处理 字符串参数 ,所以我们在传递参数的时候需要使用 JSON.stringify() 方法对对象参数序列化,在低版本IE中引用json2.js可以实现类似效果。

  2. origin:字符串参数,指明目标窗口的源, 协议+主机+端口号[+URL] ,URL会被忽略,所以可以不写,这个参数是为了安全考虑,postMessage()方法只会将message传递给指定窗口,当然如果愿意也可以将参数设置为”*”,这样可以传递给任意窗口,如果要指定和当前窗口同源的话设置为”/“

注意只有当目标窗口的源与postMessage函数中传入的源参数值 匹配 时,才能接收到消息。

接收postMessage消息

要想接收到之前源窗口通过postMessage发出的消息,只需要在目标窗口注册message事件并绑定事件监听函数,就可以在函数参数中获取消息。

window.onload = function() {
        var text = document.getElementById('txt');  
        function receiveMsg(e) {
            console.log("Got a message!");
            console.log("nMessage: " + e.data);
            console.log("nOrigin: " + e.origin);
            // console.log("Source: " + e.source);
            text.innerHTML = "Got a message!<br>" +
                "Message: " + e.data +
                "<br>Origin: " + e.origin;
        }
        if (window.addEventListener) {
            //为窗口注册message事件,并绑定监听函数
            window.addEventListener('message', receiveMsg, false);
        }else {
            window.attachEvent('message', receiveMsg);
        }
    };

可以将postMessage函数第二个参数设为*,在发送跨域消息时会跳过对发送消息的源的检查。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Linux程序设计

Linux程序设计

Neil Matthew、Richard Stones / 陈健、宋健建 / 人民邮电出版社 / 201005 / 99.00元

时至今日,Linux系统已经从一个个人作品发展为可以用于各种关键任务的成熟、高效和稳定的操作系统,因为具备跨平台、开源、支持众多应用软件和网络协议等优点,它得到了各大主流软硬件厂商的支持,也成为广大程序设计人员理想的开发平台。 本书是Linux程序设计领域的经典名著,以简单易懂、内容全面和示例丰富而受到广泛好评。中文版前两版出版后,在国内的Linux爱好者和程序员中也引起了强烈反响,这一热潮......一起来看看 《Linux程序设计》 这本书的介绍吧!

URL 编码/解码
URL 编码/解码

URL 编码/解码

MD5 加密
MD5 加密

MD5 加密工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换