You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
每每提起跨域问题,第一个想到的就是JSONP了。JSONP与JSON虽然看起来很像,但却有本质区别
。JSONP全称是JSON with Padding,并不是一种新的数据格式,而是数据格式JSON的一种“使用模式”。浏览器的同源限制对含有src属性的元素(例如: script、img)不起作用,JSONP就是利用了script的这个特性。
跨域不完全探究
前言
首先欢迎大家关注我的Github博客,也算是对我的一点鼓励,毕竟写东西没法获得变现,能坚持下去也是靠的是自己的热情和大家的鼓励。最近工作上事情比较繁多导致博客一度断更,为了挽救惨淡的关注量并兼顾自己有限的时间,准备近期多推出一些有关前端的基础知识学习,一起夯实基础。希望大家多多关注呀!
引子
跨域问题在Web开发中一直都是一个非常常见的问题,但在日常工作说实话使用频率并没有那么多,再加上重使用轻原理导致我对跨域问题理解的一直非常的浅显,借此机会让我们好好探索一下。
同源策略
跨域问题的起源于众所周知的浏览器同源策略。作为如今Web安全基石的同源策略,早在上个世纪九十年代由网景公司提出,当时仅仅只是针对于
Cookie
的访问,即不同源的网页之间Cookie
不能共享。后来同源策略变得更加严格,安全性也逐步提高,升级为:说了这么多同源,那么何为同源。通常情况下,如果一个源拥有相同的协议(protocol)、主机(host)、端口(port),则称其为同源。当然我们说了通常情况下,也就意味着这个方面有例外存在,IE毫无疑问的在这个方面承担了它固有的职责。IE并没有将端口号加入到同源策略的组成部分,因此
http://host:81
和http://host:80
是属于同源的。虽然同源的安全限制是必要的,但是同时也带了束缚,假设我确实需要向不同源的地址发送请求该怎么办呢?那就回到了我们所要讨论的跨域问题。跨域解决方案
JSONP
每每提起跨域问题,第一个想到的就是JSONP了。JSONP与JSON虽然看起来很像,但却有本质区别
。JSONP全称是JSON with Padding,并不是一种新的数据格式,而是数据格式JSON的一种“使用模式”。浏览器的同源限制对含有
src
属性的元素(例如:script
、img
)不起作用,JSONP就是利用了script
的这个特性。基本原理
前面我们说了JSONP利用的是
script
标签,绕过浏览器的同源策略,试想我们通过一个URL获取JSON数据是可能的。然而JSON数据却是不可执行的,因此JSONP通过将返回的JSON数据用函数包裹来实现执行的目的,这也就是JSONP中P表示padding(包裹)的含义。因为需要函数包裹数据,所以前后端需要提前约定好函数名,我们一般会在请求的URL中带上函数名称作为参数。例如请求接口:http://api.demo.com/data?userid=1&jsonp=parseResponse
服务器会将对应的数据填充到函数
parseResponse
:概念实现
我们实现一个最简单的函数用来创建一个JSONP请求:
然后我们用express.js来响应这个JSONP请求:
运行时你会发现浏览器会正确打印出跨域访问的数据,说明我们的跨域请求成功。
实践
当然我们并不需要在前端手动去实现该函数,很多第三方库已经封装了JSONP的请求,我们以JQuery提供的Ajax理由为例,上面的请求用JQuery去实现代码是:
我们可以看到在JQuery中提供的
ajax
函数中通过配置dataType
为jsonp
,则可以实现JSONP请求,需要注意的是JQuery只是将其封装在ajax
函数内,二者实质上有本质区别。了解JSONP的实现原理,我们知道这玩意肯定不会存在浏览器兼容性问题,毕竟我不相信会存在不支持
script
标签的浏览器。但是因为正是通过script
标签实现的,JSONP也就只能支持GET
请求,那么如果我们想实现POST
请求的跨域有什么好的办法吗?CORS
CORS是Cross-origin resource sharing的缩写,即跨域资源共享,属于W3C标准,允许跨域发送
XMLHttpRequest
请求,支持多种HTTP Method。与JSONP的原理不同的是,CORS采用的是前后端HTTP表头协商的方式(我自己起的名字)判断是否允许跨域访问,并且相比与JSONP来说,CORS需要客户端和服务端共同支持,并且整个过程由浏览器自动完成。对于前端开发者而言,跨域的CORS通信与普通的Ajax通信没有差别,整个跨域处理的过程主要集中在服务器端。CORS通信分成简单请求(simple request)和非简单请求(not-so-simple request)两种模式。简单请求
所谓的简单请求是指,请求方法属于:
HEAD
、GET
、POST
之一,并且HTTP的头信息不超出以下几种字段:如果不满足以上任一条件,均属于非简单请求。对于简单请求,浏览器会直接发送CORS请求。当我们使用Ajax发送一条跨域请求,浏览器会在HTTP请求头(Request Headers)中增加
Origin
属性:Origin
属性用来表明当前的请求来自于哪个源(协议、主机、端口)。如果服务端支持CORS跨域,则需要在响应头中返回以下属性:Access-Control-Allow-Origin
: 表示跨域的访问的源,其中"*"表示允许来自任何源的请求跨域访问Access-Control-Allow-Credentials
: 表示跨域访问是否允许带有Cookie信息,该值仅可以允许设置为true
。如果服务器不允许请求携带Cookie,则不需要发送该属性。值得注意的是,携带的Cookie信息仅仅只是所跨域的服务器域名设置的Cookie,不能携带其他域名下的Cookie信息,Cookie仍然循序同源策略。并且还需要满足Access-Control-Allow-Origin
指定的域与当前的请求的域完全一致(不能是"*")以及在XMLHttpRequest显式设置withCredentials
属性为true
,表示浏览器也允许发送Cookie。Access-Control-Expose-Headers
: 该字段可选,表示浏览器可以从该XMLHttpRequest
请求中拿到的响应头信息。默认仅能拿到Cache-Control
、Content-Language
、Content-Type
、Expires
、Last-Modified
、Pragma
这六个属性,如果想取得其他的属性,必须在该属性中指定。我们用
express.js
模拟一下:当我们前端Ajax请求:
你会发现该条请求已经不会出现跨域问题,可以正确访问到数据。
非简单请求
对于非简单请求,浏览器会预先发送一次预检请求,询问服务器是否允许跨域访问以及是否允许HTTP方法,只有得到肯定答复后,浏览器才会发出正式的HTTP请求。比如我们跨域向服务器发送一次
PUT
请求:你会发现浏览器首先会发送OPTIONS请求去预检本次跨域请求。
当预检请求检测了
Origin
、Access-Control-Request-Method
和Access-Control-Request-Headers
属性后就会正式发送请求。需要注意的是,不仅仅是上述三个属性,预检请求还会返回Access-Control-Max-Age
属性用来表示该条预检请求的有效期,在有效期内,浏览器不会再次发送预检请求,仅会使用该条缓存的预检请求。我们用
express.js
来模拟本次请求:你就会发现本次
PUT
跨域请求成功,正确返回数据,达到了我们跨域的要求。对比
与JSONP相比,CORS支持更多的HTTP方法并且整个过程由浏览器自动完成,但是仅仅只兼容IE10及以上的浏览器,如果需要兼容老版本IE浏览器,CORS可能就不适合你了。
后言
我们这边文章着重讲述了两种跨域的基本原理,但实际上围绕跨域问题还有很多值得学习的地方,比如后端对跨域请求的处理,以及前端跨域带来种种的安全性问题。其实不仅仅是JSONP与CORS,Websocket也能实现跨域请求,以后有机会可以学习一下,最后还是欢迎大家关注我的博客,水平欠佳,希望体谅,愿一同进步。
The text was updated successfully, but these errors were encountered: