CORS过程的介绍以及跨域问题

CORS过程的介绍以及跨域问题

参考文章:https://mp.weixin.qq.com/s/Re1fvKKzi-rPpu6SmpqTJA

文中的动图以及部分总结语句均引用与参考文章。我这里只是为了让自己理解深刻一点而尝试用自己的语言去总结一遍。推荐阅读原文


说到跨域问题,我们最常见的就是在我们向服务器发起请求的时候会遇到。比如说如下情况

image

假设我们正在访问 https://api.mysebsite.com 的站点,当我们点击按钮,向 https://api.mysebsite/users.com 发送请求,获取网站上的一些用户信息。这个时候从结果上是完美的。原因是,根据同源策略,我们发起请求的域名端口协议均是一至的。浏览器并不会产生跨域报错。

而如果,以上提到的三点其中一点不满足的站点,再向服务器发起请求,那么这个时候就会触发跨域报错。如下图

image

而以上两种情况出现的原因,其实就是我们今天要介绍的内容。

首先我们先来介绍一下同源策略

同源策略

浏览器网络请求的时候,有一个同源策略的机制,即默认情况下,使用API的Web应用程序只能从加载应用程序的同一个域请求HTTP资源。也就是我们上面提到的,必须要求域名,端口,协议都要一至。请求才能发送成功。而只要其中一点不一致,那么该请求也算是跨域的。

image

所以我们同样可以看得出来。同源策略会限制以下三种行为:

  1. Cookie,LocalStorage,和IndexDB 访问受限
  2. 无法操作跨域DOM(常见的如 iframe)
  3. js发起的XHR和Fetch请求受限
作用

说了这么多其实,我们可以看出同源策略的作用其实就是为了保证用户的信息安全。打个比方,如果没有同源策略,那么当你在不小心的情况下,点击了网页的钓鱼网站。然后恶意的网站很容易就能利用重定向把你带到一个iframe 的 攻击网站,这个iframe会自动加载银行网站,并通过Cookie登录你的账号。然后操作你的DOM,进行一系列的危险操作。

image

而我们当然不希望这种情况发生,这个时候同源策略就起到有效的保护作用。因为它确保了我们只能访问同一站点的资源

image

好了,接下来要说的就是CORS了。

浏览器的CORS

浏览器出于安全的考虑,会限制从脚本内发起的跨域HTTP请求。(注意是脚本内)例如XHR 和 Fetch 就遵循同源策略。这意味着使用 API 的Web应用程序只能从加载应用程序的同一个域请求HTTP资源。

image

在日常的开发中,我们很多时候都会跨域去请求别的站点的资源。而这个时候我们为了解决跨域的问题就要利用CORS机制。

CORS(Cross-Origin Resource Sharing),即跨域资源共享。如字面意思,CORS机制的存在就是为了让我们在保证安全的前提下,实现访问不同域下的资源,这算是放宽的政策。

浏览器可以利用CORS机制,放行一些符合规范的跨域访问,阻止不符合规范的跨域访问。下面我我们就来介绍一下浏览器内部是如何实现的。

Web程序发出跨域请求后,浏览器会自动向我们的HTTP header添加一个额外的字段 OriginOrigin 标记了请求的站点来源:

    GET https://api.website.com/users HTTP/1/1
    Origin: https://www.mywebsite.com -> 浏览器自己携带的
image

为了使浏览器允许访问跨域资源,服务器返回的 response 还需要加一些响应头字段,这些字段可以显式的表明此服务器是否允许跨域请求。

服务端的CORS

作为服务端人员,我们为了允许符合规则的跨域请求。我们可以通过在HTTP的响应中添加响应字段 Access-Control-* 来表明是否允许跨域请求。根据这些CORS响应头字段,浏览器可以允许一些被同源策略限制的跨域响应

虽然有好几个CORS的字段,但是有一个必须要加的字段是Acess-Control-Allow。这个头字段的值指定了哪些站点被允许跨域访问资源的。

  1. 如果我们有服务器的开发权限,我们可以给 https://www.mywebsite.com 加上访问权限,只需要把这个域名添加到 Access-Control-Allow-Origin
image

现在这个字段会被添加到服务端的响应报文中,然后返回给客户端。然后这个时候客户端再向服务端发起跨域请求,同源策略将不会再限制 https://api.mywebsite.com 站点返回的资源。

报文如下:

    HTTP/1.1 200 OK
    Access-Control-Allow-Origin: https://www.mywebsite.com
    Date: Fri, 11 Oct 2019 15:47 GM
    Content-Length: 29
    Content-Type: application/json
    Server: Apache

    {user: [{...}]}
image
  1. 收到服务器返回的response后,浏览器中的CORS机制会检查 Access-Control-Allow-Origin 的值是否等于 request 中 Origin的值
image
  1. 浏览器通过校验之后,前端成功接收到跨域站点的资源

而相反,如果对比Access-Control-Allow-OriginOrigin 的值不一致的时候,浏览器会抛出一个 CORS Error的报错信息。

image

当然,我们还可以通过配置 * 作为 Access-Control-Allow-Origin 的值,这样的话任意的外域访问都会被允许。但是这并不是解决允许多域名请求同一站点这个场景的最优解下面我会再单独讲一讲这种场景的解决方案。

除了 Access-Control-Allow-Origin 字段头之外,开发人员还可以通过其他字段对请求作出限制。比如说 Access-Control-Allow-Methods 该字段用来指明跨域请求所允许的 HTTP 方法。

image

如上图中,只有请求方法为 GETPOSTPUT 方法被允许跨域访问资源。其他的HTTP方法,例如 PATCHDELETE 都会产生预检。

这里就引申出一些别的内容,比如说预检

什么是预检呢?

预检 = 预请求(options请求)
  1. 在发起实际请求之前,如果这个请求是一个复杂请求,那么客户端会先发出一个options请求。预请求中的Access-Control-Request-* 包含了我们将要处理的实际请求信息。

首部字段 Access-Control-Request-Method 会告知服务器,实际请求要用到什么方法
首部字段 Access-Control-Request-Headers 会告知服务器,实际请求将附带的自定义请求首部字段是什么

    OPTIONS https://api.mywebsite.com/user/1 HTTP/1.1
    Origin: https://www.mywebsite.com
    Access-Control-Request-Method: PUT
    Access-Control-Request-Headers: Content-Type
image
  1. 服务器接收到预请求后,会返回一个没有 body的HTTP响应,这个响应标记了服务器允许的 HTTP 方法和 Http Header字段
    HTTP/1.1 204 No Content
    Access-Control-Allow-Origin: https://www.mywebsite.com
    Access-Control-Request-Method: GET POST PUT
    Access-Control-Request-Headers: Content-Type
  1. 浏览器收到预请求的响应,并检查是否应允许发送实际请求
image

上图漏了 Access-Control-Allow-Headers: Content-Type

  1. 如果预请求通过了,浏览器会将实际请求发送到服务器,然后服务器会返回我们想要的资源
image

而此处有一个优化点,既然每一次复杂请求都会重复 options请求,那么我们可以通过设置一个字段Access-Control-Max-Age 来缓存预请求的响应。浏览器可以使用缓存来代替发送新的预请求。

到这里,CORS和跨域的关系基本就说清楚了。下面我们再说一点补充

  1. 认证

XHR 或 Fetch 与 CORS的一个有趣的特性是,我们可以基于 Cookies 和 HTTP 认证信息发送身份凭证。一般而言,对于跨域 XHR 或 Fetch 请求,浏览器不会发送身份凭证信息。

尽管 CORS 默认情况下不发送身份凭证,但我们仍然可以通过添加Access-Control-Allow-Credentials CORS响应头来更改它

如果要在跨域请求中包含 cookie 和其他授权信息,我们需要做一下操作:

  • XHR 请求中将 withCredentials 字段设置为 true
  • Fetch 请求中将 credentials 字段设置为 include
  • 服务器把 Access-Control-Allow-Credentials: true 添加到响应头中
    // 浏览器 fetch 请求
    fetch('https://api.mywebsite,com.users', {
        credentials: "include"
    })

    // 浏览器 XHR 请求
    let xhr = new XMLHttpRequest();
    xhr.withCredentials = true;

    // 服务器添加认证字段
    HTTP/1.1 200 OK
    Access-Control-Allow-Credentials: true
image

做到了上面几点之后,就能在跨域请求中携带身份凭证了。


补充

  1. 我们上面提到可以通过设置 Access-Control-Allow-Origin 去允许跨域请求。但是如果想要允许多个域名访问同一个站点的资源的时候。我们显然是不能写死的。这个时候有就以下几个解决方案:

a. Access-Control-Allow-Origin 设置为 * (不推荐)
b. 通过nginx代理服务器进行设置
c. 修改 api 接口,在每个api上添加响应头 (待考究)
d. 拦截器方式 (微软提供的最佳方案,实际上是对c方案的封装)

d的方案就是写一个拦截器,应用到所有控制器上,在拦截器里控制来访域名,动态设置Access-Control-Allow-Origin的值.

  1. 简单请求不会触发预请求,但是会根据请求响应的报文去判断是否跨域了(即对比origin,和Access-Control-Allow-Origin的值是否一致)

  2. 简单请求这种,实际是已经发去了的请求,也就是说请求已经到了服务器了,然后返回来了。只是浏览器拦截了响应,而并不是拦截了请求本身


以上就是全部内容了

补充参考:http://www.goodpm.net/postreply/csharp/1010000008959813/AccessControlAllowOrigin%E5%A6%82%E4%BD%95%E8%AE%BE%E7%BD%AE%E5%A4%9A%E4%B8%AA%E5%80%BC%E5%91%A2.html

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