跨域与常用方案

本文源自一次内部关于跨域的讨论分享的总结

理解跨域的重点在于:了解跨域产生的场景、原理

跨域问题只在浏览器客户端环境下出现,是由于浏览器出于安全考虑设置的同源策略引起,所以解决跨域问题一般采用的思路有两种:
a. 绕过浏览器的同源策略约束
b. 遵循新的跨域规范

浏览器同源策略

同源策略:不同域的客户端在没明确授权的情况下,不能读写对方的资源
同域:协议、域名、端口号均相同
比如:

http://abc.com  vs  https://abc.com
http://ab.abc.com  vs  http://a.abc.com 
127.0.0.1:3000  vs  127.0.0.1:4000

哪些行为会受到浏览器同源策略的约束呢?

  • ajax请求 XMLHttpRequest
    注意:浏览器并不会限制跨域请求的发出,即服务端再未做其他限制的情况下仍可收到跨域请求,浏览器只是限制了ajax请求返回信息的读取、cookie写入等操作
    扩展:服务端如何限制仅接收指定域名请求? refer?
  • fetch
  • 非同域窗体之间(比如iframe引入的情况的父子窗体)不能直接进行数据通讯或共享
  • Web 字体 (CSS 中通过 @font-face 使用跨域字体资源)
  • WebGL 贴图
  • 使用 drawImage 将 Images/video 画面绘制到 canvas
    等等

哪些标签可以加载非同源资源?
script img link iframe
跨域方案的思路a也就是基于以上几种标签

JSONP -- 思路满分

json with padding

基本原理:利用script标签的src属性允许加载非同源资源,加载后js解析器将执行资源代码,目标服务器在数据外层包裹一个客户端已经定义好的函数并返回

服务端接口返回一段可执行js脚本
"hello browser" => callback("hello browser")

前后端交互流程:

jsonp前后端交互流程
  • 首先在浏览器端注册一个全局函数callback。
  • 浏览器端创建一个script标签,将src指向目标服务端资源地址,并带上callback参数,告诉服务端回调函数名称,将此script标签插入到Dom中。
  • 服务器生成返回json数据 ,获取请求中的callback函数名参数,再将json数据以参数填充的方式,和函数名参数拼接:callback+’(‘+json+’)’,最后将拼接完成的数据返回。
  • 浏览器端,解析script标签,并执行返回的javascript 文档,此时数据作为参数,传入到了客户端预先定义好的callback 函数里(动态执行回调函数)。

优缺点对比

优:

  • 兼容性极佳,在不支持XMLHttpRequest的浏览器中也可以用于模拟异步请求
    缺:
  • 只允许get请求: jsonp跨域原理是通过script标签加载跨域资源,不可能支持post请求
  • 存在一定安全隐患: 本质上来说,jsonp是一种脚本注入(Script Injection)行为
  • 需要服务端配合支持:如果一个接口同时需要适配jsonp和正常请求,需要特殊处理没有错误处理支持
  • 如果动态脚本插入有效,就执行调用;如果无效,就静默失败:不能从服务器捕捉到 404 错误,也不能取消或重新开始请求。(自行设置定时器,超时后没有进入回调即判定为请求失败 )

栗子 ——淘宝和天猫通过jsonp跨域共享cookie

在淘宝(www.taobao.com)登录后,切换到天猫(www.tmall.com),会看到顶栏已经有登录用户信息。打开控制台,刷新tmall页面,可以看到如下jsonp请求,其中第一个即为获取到登录信息的关键请求:

获取共享登录cookie信息的jsonp请求

打开该请求的Response内容:


response内容

这段返回内容(本质上是js代码)到达客户端后,将会被解析执行

利用消息通信机制实现的跨域请求

@Update :不管是post message、window.name共享、location.hash共享,这类方案的原理都是依赖消息通信机制实现的,故更改标题

以个人经常用到的Frame代理为例

基本原理:在目标服务器放置一个代理文件(proxy_frame.html),通过加载该代理文件和服务端进行数据交互(同域请求),返回数据通过消息通讯(如post message)返回给上层应用以实现跨域数据交互
a.b.com域页面

加载跨域iframe文件

实际上是利用窗体之间通讯方式 将跨域请求转化为同域请求

前后端交互流程:(以nej框架的实现为例)

iframe跨域前后端交互流程
  • 假设a.b.com的应用需要向x.y.com的服务器取数据,首先在x.y.com域服务器上预先放置代理文件,并以iframe载入该代理文件
  • 服务器端返回带配置的代理文件
  • 代理文件载入完成后,a.b.com域的应用将要发送的请求指令通过消息通信方式传递给代理文件
  • 代理文件验证a.b.com是否在预先配置的白名单中,如果不在则异常返回,否则直接发送请求至x.y.com域服务器
  • 服务器返回数据至代理文件
  • 代理文件通过消息通讯机制将请求结果返回给a.b.com域的应用

窗体间消息通信方式:

针对高版本浏览器:HTML5 Web Message
针对Trident引擎低版本浏览器(ie6-7):window.name代理(复杂结构需要stringify,启用队列修改)


image.png

image.png

优缺点对比:

优:

  • 服务端无需额外支持:仅需要放置代理用的html文件即可
  • 对请求method完整支持:get、post、put等等
  • 兼容性较好:需要一些兼容处理(主要针对消息通信方式)

缺:

  • 首次请求存在延时:由于首次发起请求时需要载入代理文件,在载入代理文件之前的所有请求都会存在一定的延时
  • 低版本浏览器并发请求延时较大:由于低版本浏览器受限于消息通讯机制,对于并发量大的请求返回时可能存在较大延时

Cross-Origin Resource Sharing规范 -- 简称CORS

为了解决跨域问题出现的标准规范
通过增加一系列请求头和响应头规范安全地进行跨站数据传输,它要求浏览器必须能支持CORS规范定义的请求头和策略执行,并且服务端需要解析这些新的请求头并按照策略返回对应的响应头和请求的资源

分为以下三种请求场景:


cors规范三种请求场景

附录:对 CORS 安全的首部字段集合

相关请求头和响应头(主要)

响应头:


响应头说明

请求头:


请求头说明

如何使用?

直接使用ajax(根据浏览器版本选择XHR或XDR对象)或fetch即可,客户端只需按规范设置请求头
服务端按规范识别并返回对应响应头,还可以对请求域名进行过滤处理
比如使用Nginx配置:(待补充)

还是看几个栗子:

客户端发起一个get请求,观察一次成功简单请求的请求头与响应头:

image.png

客户端发起一个post请求,并设置Content-Type为application/xml,观察一次成功预检+正式请求的请求头与响应头:

Options预检

Post正式

客户端发起一个post请求,并设置设置特殊标志位 withCredentials为true ,观察一次成功预检+正式请求的请求头与响应头:

Options预检

Post正式

优缺点:

优:

  • 标准 规范 安全
  • 对请求method完整支持:get、post、put等等
  • 完整的错误处理:使用XMLHttpRequest对象或FetchAPI发送请求

缺:

  • 兼容程度较低:需要高版本浏览器支持,Trident引擎的浏览器需要IE10以上才完整支持
    IE6-7 完全不支持CORS
    IE8-9 仅支持不带凭证的CORS跨域请求

其他

WebSocket

建立socket长连接,需要验证,本质上可以视为安全,不存在跨域限制
由于资源消耗较大,除了一些特殊场景,一般不使用

服务端反向代理

将本域服务端配置成 需要跨域获取的资源的 反向代理服务器
比如:使用Nginx配置请求转发:proxy_pass


反向代理

FLASH代理

与frame代理模式类似,请求通过Flash来发送(proxy_flash.swf放置在同源站),利用Flash的策略文件crossdomain.xml来控制资源的共享权限,获取目标服务器请求返回数据
---相当于把iframe改成flash

还有例如 img ping 等等等等

总结

跨域永远是无奈之举,常规情况下不应该出现

针对少量需要跨域Get请求的场景 : JSONP仍是不错的选择
针对整站大量跨域请求 :
—— 兼容性要求高: iFrame代理跨域/服务端反向代理

—— IE10以上兼容支持: CORS规范

检测浏览器支持: 高版本使用CORS规范,低版本自动降级使用iFrame代理

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,384评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,845评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,148评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,640评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,731评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,712评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,703评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,473评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,915评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,227评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,384评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,063评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,706评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,302评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,531评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,321评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,248评论 2 352

推荐阅读更多精彩内容

  • 前言:对于跨域请求,很早之前就有去了解过,但因为一直关注的都是服务器后端开发,故也就仅仅停留在概念的理解上而没有机...
    ken_ljq阅读 89,754评论 6 128
  • 题目1.什么是同源策略? 同源策略(Same origin Policy): 浏览器出于安全方面的考虑,只允许与本...
    FLYSASA阅读 1,719评论 0 6
  • 跨域问题的场景和解决方案多种多样,只要是做前端开发,总会遇到。而且面试时也是必问的问题。所以自己学习总结记录一下。...
    花开_陈凤娟阅读 736评论 0 0
  • 这篇文章我们来聊聊「跨域」,这个概念在很多公司的面试中会被问到,网上也有数不清的关于「跨域」的文章。 那我为什么还...
    水剑承王阅读 284评论 1 1
  • 我今天刚从外面回来,坐在沙发上打开手机,看到微信群里有人找我,哦,我的天呐,语文湿地把我的两篇徐水之行推送出来了!...
    聆听色彩阅读 590评论 0 1