解决跨域的两种方案JSONP和CORS

讲跨域以前,咱们先来说同源策略(SOP),同源策略是网景公司提出的一个著名安全策略。所谓同源就是域名、协议、端口相同。例如http://www.12306.cn中,http就是超文本传输协议,12306就是域名,cn就是端口。若是两个资源须要通讯,那么他必须知足SOP。而在前端中咱们使用ajax进行数据请求。
若是资源不一样域,那么咱们在使用ajax请求数据的时候,就会报错,表示拒绝访问。那如何进行跨域处理呢?事实上有三种方法一、JSONP,二、子域代理,三、CORS。因为第二种方法现在已经采用的很是少,因此咱们在这儿不作讲解javascript

1、JSONP(JSON with Padding)

带填充的JSON,是一种能够在JS中绕过同源策略,并发起跨域HTTP请求的使用模式,能够启动JS的跨域HTTP请求
同源策略有一个显著的例外,HTML脚本元素是能够规避SOP检查的。那就意味着咱们能够采用动态注入脚本的方式向其余源发出HTTP请求。JSONP正是利用了这个例外状况进行跨域数据加载的。html

一、工做原理

咱们先来看一个例子:使用ajax请求一个普通的JSON文件。假设你使用ajax请求'http://jsonpjs.com/info.json',它会返回一个JSON文档,其中包含一些信息前端

{
    'title': 'jsonp explaintation',
    'author': 'Cornelius'
}

浏览器接受到这个json文件后,就会把他当成字符串进行处理,可是这个字符串咱们须要把它转换为对象,才可以被javascript所使用,这里咱们就可使用json.parse函数来完成。固然因为同源策略的限制,ajax只可以在同一个域中才可以使用。可是正如咱们前面提到的,script是html脚本元素它能够规避SOP的检查因此咱们为了请求到json文件,咱们可使用这种方式java

<script src='http://jsonpjs.com/info.json'></script>

经过script元素请求资源,当文件加载完成时,浏览器会把json响应看成Javascript解析。可是这样的状况下咱们仍是没法得到json数据。
因为该对象没有被存储,也没有赋值给一个变量,或者做为参数传递给一个函数,浏览器就会忽略它。
那么该如何获取JSON输出呢?
这儿有两种方法,第一种直接由服务器把json数据进行存储。例若有一个外部URL,http://jsonjs.com//info.js(注意文件扩展名是.js而不是.json),内容以下git

var jsonResponse = {
    'title': 'jsonp explaintation',
    'author': 'Cornelius'
}

当文件加载完成后,咱们就能够经过全局变量jsonResponse访问这个JSON对象了。当该变量包含所请求数据时,咱们可使用script.onload来通知代码。那么另一种经过一个回调函数代替全局变量的方式来传递JSON对象github

jsonHandler({
    'title': 'jsonp explaintation',
    'author': 'Cornelius'
})

使用这种方式的好处在于,咱们不须要依靠script元素的onload事件来判断json是否可用,当info.js被解析时,回调便会自动执行。这须要加载<script>元素以前,在全局环境下定义好这个回调函数
经过<script>加载json的方法最大的缺点就是:应用程序加载这些文件时,须要预先知道全局变量或者回调函数的名称。下面是实现jsonp的简单实例ajax

window.jsonpCallback = function (json) {
    // 处理这个json数据
}
var script = document.createElement('script')
script.src = 'http://jsonjs.com//info.js?callback=jsonpCallback'
document.body.appendChild(script)

二、局限性和安全性

这种跨域技术很是的简单和强大,可是他也有一些局限性和安全性
JSONP仅适用于http的get请求。只能使用GET请求就意味着不少限制,提交到服务器的数据量将受限于浏览器的最大URL长度。JSONP缺少错误处理机制,若是脚本注入成功后,就会调用回调函数,可是注入失败后,没有任何提示。这就意味着,当JSONP遇到40四、505或者其余服务器错误时,你是没法检测出错缘由的。咱们可以作的也只有超时,没有收到响应,便认为请求失败,执行对应的错误回调。
在安全方面,借助JSONP有可能进行跨站请求伪造(CSRF)攻击,当一个恶意网站使用访问者的浏览器向服务器发送请求并进行数据变动时,被称为CSRF攻击。因为请求会携带cookie信息,服务器会认为是用户本身想要提交表单或者发送请求,而获得用户的一些隐私数据json

2、CORS

CORS是经过一系列特殊的HTTP头来解决这一问题,这些http头信息能够容许双方判断请求成功或者失败
在发送跨域HTTP请求时,支持CORS的浏览器会引入额外的Origin头信息来指定请求的源。这个头信息须要包含三个部分——协议、域名和端口
Origin: http://www.example.com
服务端的工做是检查头信息是否接受该请求。若是请求被接受,那么须要返回一个包含Access-Control-Allow-Origin,其值与客户端Origin值相同的响应头
Access-Control-Allow-Origin: http://www.example.com
若是资源是公共的容许任何源发送请求,服务器能够返回一个通配符
Access-Control-Allow-Origin: *
若是匹配成功,那么浏览器将会继续处理这个请求,不然禁止该请求。
一个使用CORS发起跨域请求的函数跨域

function makeCORSRequest(url, method) {
    if (typeof XMLHttpRequest === 'undefined') {
        return null
    }
    var xhr = new XMLHttpRequest()
    if ('withCredentials' in xhr) {
        xhr.open(method, url, true)    // 标准浏览器支持cors
    } else if (typeof XDomainRequest !== 'undefined') {
        xhr = new XDomainRequest() // 支持cors的IE浏览器
        xhr.open(method, url)
    } else {
        xhr = null // 不支持CORS的浏览器
    }
}

默认状况下,使用CORS发送请求时,浏览器不会发送任何识别信息,如cookie或者http认证头,为了发送认证信息,必须将XMLHttpRequest对象的withCredentials属性设置为true
var xhr = new XmlHttpRequest()
xhr.withCredentials = true
若是浏览器支持识别信息,那么须要在响应头中返回Access-Control-Allow-Crendentials。
Access-Control-Allow-Crendentials:true
使用Cors时,若是请求方法不是GET,POST或者HEAD,或者使用了自定义的HTTP头,浏览器将会发起所谓的预检请求,预检要求就是判断请求是否合法
客户端会发送如下信息:浏览器

Origin---请求的源
Access-Control-Request-Method---Http请求方法
Access-Control-Request-Headers----以逗号分隔的请求自定义头
而后服务器返回的请求头
Access-Control-Allow-Origin----容许的请求源
Access-Control-Allow-Methods-----以逗号分隔的容许方法列表
Access-Control-Allow-Headers------以逗号分隔的容许头信息
Access-Control-Max-Age----预检请求的缓存时间
Access-Control-Allow-Crendentials------指名所请求的资源是否支持认证信息

当客户端收到服务器返回的信息后,就会使用前面申明的http方法进行请求
不过cors预检支持颇有限:只有Firefox, Safari和Chrome。
下面是浏览器对CORS的兼容性表格

clipboard.png
个人github若是对你有帮助请点一个star
第一次写技术文章,不足的地方还请各位多多指教
参考文献:跨域资源共享 CORS 详解 ---阮一峰

[third-party javascript ---- Ben Vinegar Anton Kovalyov][2]
相关文章
相关标签/搜索