对于一个前端开发者来说,跨域是一个老生常谈的问题,不可避免,不可不谈更不能不会。最近在逐步地写项目经验总结,第一篇就从经典的跨域问题开始谈一谈。
什么是跨域
跨域问题来自于浏览器同源策略的限制,包括DOM同源限制和ajax同源限制,本文探讨的是ajax跨域。ajax跨域指的是一个页面的ajax只能请求和当前页面同源的数据,如果发现请求到的数据不符合要求,浏览器就会阻止返回的数据。所谓同源,指的是协议、域名、端口号都必须完全相同(同一ip的不同域名也是跨域)。同源策略的主要目的是防止csrf攻击,它可以有效地避免由于恶意攻击带来的危险,浏览器器同源策略使得网络访问更加安全。
但是,实际开发与生产中,常常获取使用来自其他站点的资源,这时候就需要发起跨域请求,这时候就需要使用特殊的方法来处理,使得我们能够获得想要的数据。
由此可知,跨域仅限于浏览器中,是由于浏览器对不同源数据的拦截产生的,跨域有时候是不可避免的,我们需要采取措施实现跨域请求。
跨域请求的方式
实现跨域请求有很多方式,以下是我知道的几种常见的方法:
JSONP
首先最有名的一种方式就是jsonp,在学习jsonp之前首先要知道虽然浏览器有同源限制,但是有三个标签是不符合这种限制的<img>
的src(获取图片),<link>
的href(获取css),<script>
的src(获取javascript),这是由他们的自身特性所决定的。而jsonp,就是利用了script标签不限制同源的特点来实现的。
清楚了jsonp的原理之后,再看jsonp其实很简单了,下面来看一个简单的小例子。
假设客户端需要获取的json数据{code: 200, data: “success”},一个简单的服务端实现如下(使用node.js原生http模块)
1 | const http = require('http'); |
客户端的请求如下
1 | <script> |
综合客户端和服务端的代码,可以看出,在客户端,通过请求参数传递一个jsonp方法名,在服务器端,返回的结果使用指定的jsonp方法调用来包装,这样相当于请求了一段js,而真正的返回结果可以通过函数调用参数来获取,这样就可以绕开浏览器同源限制,获取跨域请求结果。
jsonp是一种常用的跨域方式,目前有很多前端的jsonp请求封装,它们通过通过动态创建script标签来实现,我们可以直接调用。jsonp方式兼容所有的浏览器,但是只支持get请求。
CORS
CORS中文是“跨域资源共享”(Cross-origin resource sharing),是W3C支持的一种新的跨域方式,它与其它的方式不同的是,它是写入标准的跨域请求方式,现代浏览器普遍支持。它允许在服务器支持的前提之下,像发起普通ajax请求一样发送跨域请求。除了get请求CROS支持其它种类请求。
CORS请求分为简单请求和非简单请求两种,简单请求需要满足以下两个条件:
请求方法是以下三种方法之一:
- HEAD
- GET
- POST
HTTP的头信息不超出以下几种字段:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
除此之外都是复杂请求,对于简单请求和复杂请求浏览器的处理方式不同。
对于简单请求,浏览器会在请求头中添加Origin字段来指明本次请求源,服务器会对发来的请求进行检查,对于符合条件的请求,服务器会在返回头信息中添加Access-Control-Allow-开头的相关字段。
对于非简单请求,正式请求之前会增加一次OPTIONS请求来进行”预检”,此次请求会带上请求源,服务器会校验是否符合条件,如果不符合会返回一个不带任何CORS相关的头信息字段,浏览器就会知道请求不允许,触发错误,停止继续发送请求。
CROS重点就在服务器上,只要配置了允许CROS,就可以正常发送请求,非常方便且安全性好,具体的服务器配置取决于服务端的不同实现。
服务器代理
这是一种终极的解决方案,因为限制只存在于浏览器中,在其他环境下是不存在的,服务器中中自然也不存在,所以只需要在服务器做好请求代理,请求变成同源的自然就不存在问题了。
服务器代理可以采用nginx作为代理,也可以使用nodejs中间件等方式,无论哪种方式,其目的都是绕开浏览器的限制,在浏览器视角来看,还是同源访问,自然也就不存在跨域的问题了。
以上是ajax跨域的主要方式,对于另一种跨域–页面之间跨域交互限制,还有其特定的解决方案,常用的方式有document.domain,window.name,postMessage几种方案,由于现阶段我接触的不够多,等到后面有实际操作经验后再补充文章。本文只探讨ajax跨域。
补充:其他的跨域解决方案
由于经验尚浅,以下内容本人并未在开发中使用过,作为理论学习经验写在这里,欢迎指正。
iframe跨域
浏览器同源限制另一个问题就是:浏览器中不同域的框架之间不能进行js的交互操作,一下几种方式,就是为了解决这一限制的。
document.domain来跨子域
这种方式适用于主域名相同,而子域名不同的情况。如a页面http://a.test.com,b页面http://b.test.com,此时向跨域访问,可以分别给两个页面设置document.domain=test.com,之后通过parent或者window['iframename']等方式就可以跨域操作iframe了。
window.name跨域
window对象有个name属性,该属性有个特征:即在一个window的生命周期内, window载入的所有的页面都是共享一个window.name的,即使页面甚至域名都不同。每个页面对window.name都有读写的权限,并且name长度可以达到2MB,这就提供了一个跨域共享资源的机会。
location.hash跨域
location.hash跨域利用了子框架具有修改父框架src的hash值,且页面不会刷新这一特点。利用这种方式传递的数据的字节数是有限的。
HTML5的postMessage跨域
window.postMessage(message,targetOrigin)是一个HTML5的api,允许两个窗口之间进行跨域发送消息,它可以实现页面和其打开的新窗口的数据传递,多窗口之间消息传递,页面与嵌套的iframe消息传递等情景下的跨域问题。
websocket协议跨域
websocket是一种不同于http的新的协议,它实现了浏览器与服务器全双工通信,同时允许跨域通讯,有关websocket相关内容我在之前文章中提到过,在此不再过多讨论。
尊重原创,转载分享前请先知悉作者,也欢迎指出错误不足共同交流,更多内容欢迎关注作者博客点击这里