弄懂CORS

OK but no!

原文链接:https://medium.com/@baphemot/understanding-cors-18ad6b478e2b


如果你在前端使用过AJAX,你应该对下面出现在浏览器控制台里的错误很熟悉。如果你没见过,那只能说明你还年轻。


Failed to load https://example.com/: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘https://anfo.pl' is therefore not allowed access. If an opaque response serves your needs, set the request’s mode to ‘no-cors’ to fetch the resource with CORS disabled.


如果你看到这个错误信息,这表示这次返回数据失败了,但是你仍然可以打开浏览器的Network栏,看到返回的数据——这到底是怎么回事?

Cross-Origin Resource Sharing (CORS)

上面的错误是因为浏览器的CORS机制导致的。COSR(跨站点资源分享)通俗的讲是跨域问题,严格来说它是跨域问题的解决方案之一,而且是官方解决方案。


在CORS成为标准之前,是没有办法请求不同域名的后端API的,因为安全原因。请求会被同源策略阻止,现在也是。


CORS是一种可以让你实现跨站点请求并同时阻止恶意js的请求,它会在你发送下面几种HTTP请求时触发:

- 不同的域名 (比如在网站 example.com 请求 api.com)

- 不同的子域名 (比如在网站 example.com 请求 api.example.com)

- 不同的端口 (比如在网站 example.com 请求 example.com:3001)

- 不同协议 (比如在网站 https://example.com 请求 http://example.com)

这个机制阻止攻击者在一些网站上放置js脚本(比如通过Googls Ads展示的广告)发起一个AJAX请求访问www.yourbank.com,假设你刚好登陆过这个网站,就可能使用你的验证信息发起一笔转账。

如果你的浏览器发起一个“非简单”请求(比如这个请求里包含了cookies,或者Content-type不包含application/x-ww-form-urlencodedmultipart/form-data 或者 text-plain)一个叫做预检查的机制会发送一个OPTIONS请求到服务器。如果服务器没有返回带有特殊头部的数据,简单请求GET或者POST请求仍然会发送,服务器的数据也会返回,但是浏览器会阻止Javascript获取这次请求。

如果明确的需要在一个请求里添加cookies,自定义头部信息或则其他特性,这将不在是一个简单请求,并且服务器没有适当的返回,这次请求讲不会发送。就是复杂请求时,如果OPTIONS的请求,服务器没有做出适当的返回,后面真实的请求将不会发送。

Access-Control-Allow-What?

CORS使用一些HTTP头信息——包括请求和返回——为了让工作继续开展下去,你必须了解一下的一些头信息:

Access-Control-Allow-Origin

这个头部信息由服务器返回,用来明确指定那些客户端的域名允许访问这个资源。它的值可以是:

- * —— 允许任意域名

- 一个完整的域名名字(比如:https://example.com)

如果你需要客户端传递验证信息到头部(比如:cookies)。这个值不能为 * —— 必须为完整的域名(这点很重要)。

Access-Control-Allow-Credentials

这个头部信息只会在服务器支持通过cookies传递验证信息的返回数据里。它的值只有一个就是 true。跨站点带验证信息时,服务器必须要争取设置这个值,服务器才能获取到用户的cookie。

Access-Control-Allow-Headers

提供一个逗号分隔的列表表示服务器支持的请求数据类型。假如你使用自定义头部(比如:x-authentication-token 服务器需要在返回OPTIONS请求时,要把这个值放到这个头部里,否则请求会被阻止)。

Access-Control-Expose-Headers

相似的,这个返回信息里包含了一组头部信息,这些信息表示那些客户端可以使用。其他没有在里面的头部信息将会被限制(译者注:这个头信息实战中使用较少)。

Access-Control-Allow-Methods

一个逗号分隔的列表,表明服务器支持的请求类型(比如:GET, POST)

Origin

这个头部信息,属于请求数据的一部分。这个值表明这个请求是从浏览器打开的哪个域名下发出的。出于安全原因,浏览器不允许你修改这个值。

如何修复CORS“错误”?

你应该明白了CORS的行为并不是一个错误——它是一个机制,用来保护你的用户,你和你请求的服务器。

有时,缺乏适当的头部信息是因为客户端实现错误(比如:丢失验证信息比如API key)。

下面有几个适应不同情况,“修复这个错误”的方法:

A——我在开发前端并且可以控制或者认识开发后端的人员

这是一个最好的情况——你应该能让返回信息的头里包含适当的CORS字段。如果你请求的API使用node的experss,你可以使用cors包。如果你想让你的网站更加的安全,你应该使用白名单来返回Access-Control-Allow-Origin头。

B——我在开发前端,但是我不能控制后端,我需要一个临时方案

这是第二个最好的情况,特别是在有时间限制的情况里。临时的解决这个问题可以让你的浏览器忽略CORS机制——比如安装ACAOChrmoe插件或者启动Chrome时输入下面的指令:

chrome --disable-web-security --user-data-dir

重要:需要记住的是,这个方法会关闭整个浏览器的CORS机制,包含你浏览器正在访问的网站,要小心使用,非常不安全。(译者注:这个方法没有用过,个人觉得风险太大,临时测试也慎用,怕你开了插件忘记关掉)

其他方法可以使用devServer.proxy(假设你使用webpack来启动你的app)或者使用CORS-as-a-service解决方案,比如https://cors-anywhere.herokuapp.com/

C——我在开发前端,但是我控制不了后端,而且将来也控制不了

好的。事情越来越复杂了。首先,你应该思考,为什么服务器没有返回适当的头部。

也许你请求的API不许允第三方应用请求它们?或者这些API仅仅给APP使用,而不是浏览器?或者你应该发送一个验证token在你请求的URL里?

假如你坚持要通过浏览器获得它们的数据,你应该自己写一个代理,在浏览器和你要请求的API之间。就像我们在方法B里做的那样。


在中间加一个代理

这个代理没有运行在和你应用相同的域名下,但是这个代理为你的请求提供正确的CORS响应。这个代理去请求API时就不需要CORS支持,因为这个代理不是用浏览器去访问的API,而是通过程序直接发起的请求。

你可以根据你的平台实现这个代理,或者使用已经做好的解决方案,比如:https://www.npmjs.com/package/cors-anywhere

请记住,如果你想支持用户验证,这些方法会引入安全风险。

译者注:实战中,能控制服务器的情况下。最好是服务器上正确配置CORS,可以在服务器API层进行配置,也可以在nginx或者apache层进行配置(这样后端新加服务器不用再配置)。最好配置上白名单。真实项目中,CORS问题主要出现在开发阶段,本地启动的前端开发服务器域名是localhost。调试接口可能是一个其他域名,这个时候的解决方法。AC都可以,不过C方法会导致每个前端项目都需要自己的开发服务器支持一个proxy。个人偏向去测试环境的nginx服务层配置跨域,这样,开发环境统一支持前端本地开发跨域调试接口。

更多关于CORS

如果你希望学习更多关于CORS的细节,请访问MDN article

推荐一个模拟接口的应用,可以在你本地开发前端,但是后端你不能直接访问或者还没写好时,模拟调试你的接口https://www.npmjs.com/package/back-mock

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

推荐阅读更多精彩内容