讲讲同源策略以及跨域解决方案

讲讲同源策略以及跨域解决方案

本文参考:

  1. 浏览器的同源策略
  2. 九种跨域方式实现原理(完整版)

同源策略

同源限制策略是浏览器的行为,对于一个网页 (html),当它内部的脚本发起一个请求时,浏览器会对这个请求的资源做同源检测

只有协议、域名、端口三者都一致的,才算是同源,才能够正常拿到响应数据,否则,都称为跨域请求,浏览器会将响应结果进行拦截,请求虽然是发出去了,但代码上读取不到响应

以下这些操作都会进行同源限制策略:

  • XMLHttpRequest 请求和 fetch 请求
  • Web 字体(CSS 中通过 @font-face 加载的字体)
  • WebGL 和 Canvas 绘制 image/video

跨域解决方案

解决方案有很多种,比如 jsonp,CORS,websocket,nginx 等等,每种方案基本都有各自的适用场景,优缺点,目前普遍场景用的是 nginx + CORS

方案一:CORS

CORS:跨域资源共享,通过响应头中的 Access-Control-Allow-Origin 字段来声明,本资源是否允许外域访问

这种方案需要依赖于后端,请求的资源可以是某个资源文件,也可以是 json 接口,但后端需要对这些请求的响应头中添加字段,来声明,本资源或接口允许外域访问

这样,浏览器在看到响应头中有这个字段,就不会对跨域请求的响应结果做拦截

这是一种根本解决方案,缺点是需要依赖后端

方案二: nginx

借助 nginx 可以有两种方案,一种是直接用 nginx 做代理转发,另一种则是结合 CORS

  • 代理转发

浏览器有同源限制,那只要保证请求的是同源的也就没问题了

假如 http://domain1.com 的网页里有对 http://domain2.com 的请求,因为域名不一样,请求的结果被浏览器拦截了

借助 nginx 的转发,那么,前端可以这么处理跨域的请求:将所有对 http://domain2.com 也就是非本源的请求,都修改成类似 http://domain1.com/domain2 的请求,也就是使用同协议、同域名、同端口,但根据不同路径来区分,这样就不存在跨域问题

前端这样修改后,因为实际请求是需要发送到 http://domain2.com 的,所以,就需要 nginx 在接收到请求时,根据不同路径,将请求转发到真正的目的地,拿到结果后再返回

这种方案,需要前端配合,让前端访问一个同源的虚假 url,再交由 nginx 做转发,去访问真正的 url

缺点就是,划分的路径需要区分同源的原本请求以及虚假 url 请求

这种方案也挺常用的

  • 结合 CORS

只利用 nginx 代理转发的话,就需要保证所有请求都是同协议、同域名、同端口的,只能根据路径不同做区分,这样灵活性不高

所以,还可以结合 CORS,因为只要响应头中返回跨域资源共享的相关字段,浏览器就不会进行同源限制

所以,只要保证前端的请求都经过 nginx,那么协议、域名、端口一不一样无所谓了,在 nginx 上配置这些跨域请求返回的响应头中都加上跨域资源共享允许的字段,就可以了

这种方案其实就是利用 nginx 的转发 + CORS,是目前比较常用的一种

方案三:jsonp

jsonp 原理是利用 script 标签没有同源限制,依赖服务端的一种方案,局限性是只能发送 get 请求

浏览器在遇到 script 标签时,会去访问 src 属性的地址,将 js 文件下载下来,并执行

那么,只要把 src 属性赋值需要跨域请求的 url,然后本地先声明一个 callback 函数,这样,服务端在接收到请求时,返回的 js 代码就是调用 callback,需要返回的数据以参数传递给 callback,浏览器在下载完这 js 并执行时,就等效于调用了 callback

所以这方案需要依赖于后端,前端需要告知后端这请求的回调函数名,由后端来写调用的代码

方案四:postMessage

这个是 HTML5 中新增的可以用于跨文档消息传递使用的 api

没用过,不清楚,但应该是适用于在多个网页间,或者有 iframe 嵌套网页间的消息传递,对于普通的跨域访问后端的场景好像不适用

方案五:websocket

websocket 是 HTML5 的一个持久化协议,实现了浏览器与服务器的全双工通信,双方都可以主动发送数据,没有同源的限制

有它的适用场景,对于普通的调接口,一次性拿数据这种场景,应该也不适用

其他方案

还有一些比如 window.name + iframe 或者 location.hash + iframe 或者 document.domain + iframe 等方案,没遇到过,也没深究过,暂不了解

请叫我大苏 wechat
您的支持将鼓励我继续创作!