跨域

443 阅读4分钟

这是我参与更文挑战的第1天,活动详情查看: 更文挑战

同源策略

同源策略限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。是一个用于隔离潜在恶意文件的关键的安全机制。浏览器出于安全的考虑只允许跟本域下面的接口进行交互,不同域下,在对方没有允许的前提上,不能读写对方的资源。

本域指的是同协议、同域名以及同端口。

跨域的方法

JSONP(JSON with Padding)

<script> 标签不受同源策略的限制,可以载入任意地方的 JavaScript 文件(或者其他文件),将其内容当做 js 执行。

所以 JSONP 的理念是跟服务器约定一个函数名,当请求服务器的时候,服务器返回一段 JavaScript 代码,这段JavaScript 调用了约定好的函数,将其数据当做参数传入该约定好的函数,因此在浏览器端需要有该约定函数的实现,否则会报约定函数 undefined 的错误。

举个例子:

需要获取数据的页面 index.html

<script>
  function getNews(data){
    console.log(data);
  }
</script>
<script src="http://aaa.com/news.js"></script>

aaa.com/news.js 的内容是:

getNews({
    "newId":  "1001",
    "newsName": "跨域的方法"
})

于是,在浏览器端定义了 getNews(data) 的函数后,浏览器端将 aaa.com/news.js 的内容直接载入到 index.html 中,相当于:

<script>
  function getNews(data){
    console.log(data);
  }
</script>
<script>
  getNews({"newId":  "1001", "newsName": "跨域的方法"});
</script>

很显然,在上述脚本中,将 {"newId": "1001", "newsName": "跨域的方法"} 当做参数传入定义好的 getNews(data) 方法中,执行此方法即可。

若浏览器向服务器端请求调用接口的话,需要将约定的函数名使用 callback 传递给服务端,服务端去解析 callback 这个参数获取到的函数名,将要返回的数据使用该函数名包裹返回给浏览器,后续如上述操作。故 JSONP 需要对应的接口的后端配合才能实现。

CORS(Cross-Origin Resource Sharing)

CORSW3C 推出的一种跨域的访问的验证机制。这种机制让 web 应用服务器能支持跨站访问控制,使跨站数据传输更加安全,减轻跨域 HTTP 请求的风险。

目前主流浏览器都已基本提供对跨域资源共享的支持,IE 支持到版本 10 以上,移动端浏览器也几乎全部支持。

当使用 XMLHTTPRequest 发送请求时,浏览器若发现请求不符合同源策略,会给该请求加一个请求头:Origin,后台进行一系列处理,如果确定接受请求则在返回结果中加入一个响应头:Access-Control-Allow-Origin;浏览器判断该响应头中是否包含 Origin 的值,如果有浏览器则会处理响应,同时我们会拿到响应数据;如果不包含,浏览器则直接驳回,这时我们就无法拿到数据了。

可以设置的响应头信息:Access-Control-Allow-Origin

Access-Control-Allow-Origin: <origin> | *

origin 表示被允许跨域访问这个资源的网站,* 代表全部网站。浏览器会检测这个参数,如果符合要求,才会去获取资源。

注:上述两种跨域方式(JSON 和 CORS)都需要服务端的配合才能实现。

降域(document.domain

document.domain 默认的值是整个域名,所以即使两个域名的二级域名一样,他们的 document.domain 也不一样。

降域使用的条件:

  1. 调用另一个页面的 window 对象
  2. 二级域名相同
  3. 协议相同
  4. 端口相同 降域的使用方法就是将符合上述条件的页面中的 document.domain 设置为同样的二级域名,这样就可以使用其他页面中的 window 对象做一些操作。

注:

  1. x.one.example.comy.one.example.com 可以将 document.domain 设置为 one.example.com,也可以设置为 example.com。
  2. document.domain 只能设置为当前域名的一个后缀,并且包括二级域名或以上(.edu.cn 这种整个算顶级域名)。

postMessage

HTML5 中,window 对象添加了方法 postMessagewindow.postMessage() 方法被调用时,会在所有页面脚本执行完毕之后向目标窗口派发一个 MessageEvent 消息。

otherWindow.postMessage(message, targetOrigin, [transfer]);

  1. otherWindow 接受消息的 Window 对象。
  2. message 将要发送到其他 window 的数据,新的浏览器里面可以传递对象。
  3. targetOrigin 通过窗口的 origin 属性来指定哪些窗口能接收到消息事件,其值可以是字符串*(表示无限制)或者一个URI
  4. transfer 可选,是一串和 message 同时传递的 Transferable 对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。
  5. 这个方法非常强大,无视协议、端口、域名的不同。
var windowObj = window; // 可以是其他的 Window 对象的引用
var data = null;

addEventListener('message', function(e){
    if(e.origin == 'http://jasonkid.github.io/fezone') {
        data = e.data;
        e.source.postMessage('Got it!', '*');
    }
});

上述的 message 事件就是用来接收 postMessage 发送过来的请求的。函数参数的属性有以下几个:

  • origin 发送消息的 window 的源。
  • data 数据。
  • source 发送消息的 Window 对象。 注:降域和 postMessage 只用于浏览器之间,多用于当前页面跟页面中的 iframe 进行通信,做一些页面嵌套的操作。降域需要域名相同,而 postMessage 可以使用在任意有 window 对象的情况下。

附:手写ajax

let xhr = new XMLHttpRequest()
xhr.open('GET', '/xxx')
xhr.onreadystatechange = function(){
    if(xhr.readyState === 4 && xhr.status === 200){
        console.log(xhr.responseText)
    }
}
xhr.send('a=1&b=2')

最后说一句

如果这篇文章对您有所帮助,或者有所启发的话,帮忙关注一下,您的支持是我坚持写作最大的动力,多谢支持。