什么是域?

出于安全的考虑,浏览器同源策略会限制来自不同域名、协议、端口(不同的域)资源之间的访问,这个时候,我们就需要跨域访问其他不同域上服务器的资源。

下表展示了相对 http://bbs.mafengshe.com/a/b.html 同源检测的示例:

URL 结果 原因
http://bbs.mafengshe.com/a/b/c.html 成功 相同域名
http://bbs.mafengshe.com/b/c.html 成功 相同域名
https://bbs.mafengshe.com/a/b.html 失败 不同协议
http://bbs.mafengshe.com:90/a/b.html 失败 不同端口
http://work.mafengshe.com/a/b/c.html 失败 不同域名

另外,如果是协议(http和https)或者端口的不同,前端js是无法实现跨域访问的,需要后台实现。

如何跨域?

想要跨域,首先要从同源策略下手,突破同源策略的限制

使用document.domain更改源

我们可以通过document.domain设置当前域的值为自身或者是当前域的父级,设置成父域后,较短的域将用于后续的检查。

假设在 http://bbs.mafengshe.com/a/b.html 域下执行下面语句

document.domain = 'mafengshe.com'

这样,页面就能通过对 http://mafengshe.com/a/other.html 的同源检测。不过,document.domain 无法把 mafengshe.com 设成 otherdomail.com

浏览器单独保存端口号。任何的赋值操作,包括document.domain = document.domain都会以null值覆盖掉原来的端口号。因此company.com:8080页面的脚本不能仅通过设置document.domain = "company.com"就能与company.com通信。赋值时必须带上端口号,以确保端口号不会为null。

注意:使用document.domain允许子域安全访问其父域时,您需要设置document.domain在父域和子域中具有相同的值。这是必要的,即使这样做只是将父域设置回其原始值。否则可能会导致权限错误。

使用document.name 存储信息

window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面(甚至不同域名)都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的,所以我们可以通过window.name在不同iframe间传递信息,name可以存储2MB的信息。

通过JSONP实现跨域

JSONP(JSON with Padding)是JSON的一种“使用模式”,而 HTML 的<script> 元素是一个例外。利用 <script> 元素的这个开放策略,网页可以得到从其他来源动态产生的 JSON 资料,而这种使用模式就是所谓的 JSONP。用 JSONP 抓到的资料并不是 JSON,而是任意的JavaScript,用 JavaScript 直译器执行而不是用 JSON 解析器解析。

JSONP(JSON with Padding)是JSON的一种“使用模式”,JSONP由回调函数和数据组成,该数据就是函数的参数。

因为同源策略的缘故,我们无法访问访问非同源的资源,但是HTML 的<script> 元素是一个例外。利用 <script> 元素的这个开放策略,网页可以得到从其他来源动态产生的 JSON 资料,而这种使用模式就是所谓的 JSONP。用 JSONP 抓到的资料并不是 JSON,而是任意的JavaScript,用 JavaScript 直译器执行而不是用 JSON 解析器解析。

/**原理如下:
*  通过script标签请求js
*  后台获取get的参数
*  后台返回带上参数的fetchData(data)
*  浏览器在script请求完成后,返回的函数会被执行,参数就传过来了
*  至此跨域通信完成
**/
function fetchData(res){
    console.log('The responsed data is: '+ res.data);
}
var script = document.createElement('script');
script.src = 'http://www.baidu.com/json/?callback=fetchData';
document.body.insertBefore(script, document.body.firstChild);

JSONP的优缺点

JSONP的优点是:它不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制;它的兼容性更好,在更加古老的浏览器中都可以运行,不需要XMLHttpRequest或ActiveX的支持;并且在请求完毕后可以通过调用callback的方式回传结果。

JSONP的缺点则是:它只支持GET请求而不支持POST等其它类型的HTTP请求;它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题。

优点

  • 不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制
  • 兼容性更好,在更加古老的浏览器中都可以运行,不需要XMLHttpRequest或ActiveX的支持
  • 数据调用获取简单

缺点

  • 只支持GET请求而不支持POST等其它类型的HTTP请求

  • 它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题

  • 安全性不能保证

  • 调用失败的时候无法返回错误码

跨域资源共享(CORS)

什么是CORS?

CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。

它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。

CORS实现跨域的原理

CORS背后的思想,就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是成功还是失败

CORS跨域的实现需要前浏览器端和服务器端共同支持。服务器通过设置Access-Control-Allow-Origin的值为可被允许的源或者*,当浏览器获取到该头字段信息,就会允许AJax跨域访问了。

对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。

因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

非简单请求

首先先介绍简单请求这一概念;满足以下条件即为简单请求

  • HTTP方法是下列之一
    HEAD
    GET
    POST
    
  • 不能人为设置除下面以外的请求头:
    Accept
    Accept-Language
    Content-Language
    Content-Type (需要注意额外的限制)
    DPR
    Downlink
    Save-Data
    Viewport-Width
    Width
    
  • 请求头中的Content-Type请求头的值是下列之一
    application/x-www-form-urlencoded
    multipart/form-data
    text/plain
    

不满足以上要求的请求,即为非简单请求。非简单请求是可能修改服务器资源的请求,比如请求方法是PATCH,PUT或DELETE; 非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。"预检"请求用的请求方法是OPTIONS,表示这个请求是用来询问的。

服务器收到"预检"请求以后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应。

服务器端需要返回注入下面的 response header:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, PATCH 
Access-Control-Allow-Headers: X-Custom-Header

一旦服务器通过了"预检"请求,以后每次浏览器正常的CORS请求,就都跟简单请求一样,进行跨域资源访问。

与JSONP的比较

  • JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求
  • 使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理
  • JSONP主要被老的浏览器支持,它们往往不支持CORS,而绝大多数现代浏览器都已经支持了CORS)

使用HTML5的window.postMessage方法跨域

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

results matching ""

    No results matching ""