跨域资源共享Cors

跨域资源共享Cors

CORS是什么

跨域资源共享(CORS,Cross-origin resource sharing),是W3C标准,是一种机制,它使用额外的HTTP头来告诉浏览器,让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域或端口请求一个资源时,资源会发起一个跨域 HTTP 请求

     http://localhost:9000请求http://localhost:8761/eureka/apps就是违背了上述原则,即:请求服务器不同端口的另一个资源,出于安全原因,浏览器限制发起的跨源HTTP请求,则会出现本文开头提到的现象及异常。

     例如,XMLHttpRequest和Fetch API遵循同源策略, 这意味着使用这些API的Web应用程序只能从加载应用程序的同一个域请求HTTP资源,除非使用CORS头。

      跨域资源共享( CORS )机制允许 Web 应用服务器进行跨域访问控制,从而使跨域数据传输得以安全进行。浏览器支持在 API 容器中(例如 XMLHttpRequest 或 Fetch )使用 CORS,以降低跨域 HTTP 请求所带来的风险。

面对CORS的限制,将如何解决呢

世间万物完事,有因必有果,有果必有因。当然CORS的限制,官方也是给出了解决办法的。

     CORS标准新增了一组 HTTP 头字段(Access-Control-Allow-Origin),允许服务器声明哪些源通过浏览器有权限访问哪些资源。另外,规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括Cookies 和 HTTP 认证相关数据)。

      CORS请求失败会产生错误,但是为了安全,在JavaScript代码层面是无法获知到底具体是哪里出了问题。你只能查看浏览器的控制台以得知具体是哪里出现了错误。

浏览器的同源策略

首选,跨域是由于浏览器端的同源策略限制所得来。

同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。
那么,何为同源呢?只有当协议、端口、域名都相同的页面,则两个页面具有相同的源。只要网站的协议protocol、 主机host、 端口号port 这三个中的任意一个不同,网站间的数据请求与传输便构成了跨域调用,会受到同源策略的限制。

浏览器的同源策略,出于防范跨站脚本的攻击,禁止客户端脚本(如 JavaScript)对不同域的服务进行跨站调用(通常指使用 XMLHttpRequest 请求)。
 

解决办法

跨域请求方式

解决跨域问题,最简单的莫过于通过 Nginx 反向代理 进行实现,但是其需要在服务器层面修改,且有可能请求的资源并不再我们控制范围内(第三方),所以该方式不能作为通用的解决方案,下面阐述了经常用到几种跨域方式:

各路解决方案中,最为典型的有两个:

JSONP和同域代理;

同域代理就是使用Ajax向同域下的后台发送请求,同时携带真实请求的地址及参数,后台接受请求后直接根据地址及参数转发请求,因为后台是可以直接模拟HTTP客户端发送请求的,所以没有跨域问题,而后台接受到响应数据后再原样返回给前端浏览器,从而实现跨域数据交互;

JSONP是利用了 script 标签的 src 属性来实现跨域数据交互的,因为浏览器解析HTML代码时,原生具有src属性的标签,浏览器都赋予其HTTP请求的能力,而且不受跨域限制,使用src发送HTTP请求,服务器直接返回一段JS代码的函数调用,将服务器数据放在函数实参中,前端提前写好响应的函数准备回调,接收数据,实现跨域数据交互;

JSONP 是目前应用最为广泛的技术解决方案,我们使用的 百度、淘宝、360搜索等各大搜索引擎的关键字推举都在使用JSONP技术;

JSONP和同域代理,本质上并没有解决Ajax跨域的问题,只是绕开这个问题而另辟蹊径实现的跨域数据交互,在数据交互层面上可以看做技术不成熟时的临时解决方案;但是JSONP 和同域代理 使用了很多年,当然跨域问题也存在了很多年,终于有人看不下去了,提出了浏览器与服务器跨域通信的安全性通信策略,它就是我们今天的主角 跨域资源共享(Cross-origin resource sharing),简称 CORS ; 有了它,大家可以安全放心的抛弃 JSONP 和 同域代理了,步入正统的跨域数据交互的殿堂;

CORS 的使用得益于 HTTP-1.1 协议版本的推广的大面积使用,它由一系列传输的HTTP头组成,这些HTTP头有两个作用,

1:用于阻止还是允许浏览器向其他域名发起请求

2:用于接受还是拒绝其他域名返回的响应数据

也就意味着,我们只要搞清楚什么样的头信息是控制浏览器发送还是不发送请求,什么样的头信息控制浏览器接受还是拒绝服务器的响应数据,这两点搞明白,CORS就算彻底搞清楚了;

我们先从一个普通的Ajax跨域说起,可能还有人不知道这个事情,就是Ajax在发送跨域请求时,请求是能够正常发送出去的,同时服务器也是可以正常接收并能够做出响应的:

再次强调,跨域是浏览器正确做出请求,服务器也能正确做出响应,是浏览器拒绝接收跨域服务器返回的数据;

而在上面的请求头中,我们能够发现与普通的HTTP请求不一样的是,添加了Origin请求头,它指示了请求来自于哪个站点,这个请求头是浏览器在发现本次为跨域请求时自动添加的,目的就是告知服务器本次请求是那个域发起的,而此时,如果服务器允许本次请求响应的数据是可以共享的,那么服务器需要添加一个 Access-Control-Allow-Origin 响应头,并指明可以共享数据的域,如下:

而Access-Control-Allow-Origin 响应头的值,可以设置为“*” (星号),表示可以与任意域进行数据共享

很多人也认为使用CORS解决跨域很简单,只需要在服务器添加响应头 “ Access-Control-Allow-Origin :* ” 就可以了,

其实不然,因为在CORS中,所有的跨域请求被分为了两种类型,一种是简单请求,一种是复杂请求 (严格来说应该叫‘需预检请求’);简单请求与普通的ajax请求无异;但复杂请求,必须在正式发送请求前先发送一个OPTIONS方法的请求已得到服务器的同意,若没有得到服务器的同意,浏览器不会发送正式请求

上面的请求其实就是个简单请求类型;那么简单类型和复杂类型是如何区分的呢?

满足以下所有条件,被视为简单类型的请求:

1:请求方法必须是 GET、HEAD、POST中的一种,其他方法不行;

2:请求头类型只能是 Accept、Accept-Language、Content-Language、Content-Type,添加其他额外请求头不行;

3:请求头 Content-Type 如果有,值只能是 text/plain、multipart/form-data、application/x-www-form-urlencoded 中的一种,其他值不行;

4:请求中的任意 XMLHttpRequestUpload 对象均没有注册任何事件监听器;

5:请求中没有使用 ReadableStream 对象。

而以上的条件有任意一条不满足,则视为复杂类型的请求;前面说过,如果是复杂请求,浏览器会先发送OPTIONS方法的请求以取得服务器的确认,如下,我随意的添加了一个请求头:

我们可以清楚的看到,浏览器做出了跨域拦截的处理,这是因为我们添加了额外的请求头信息,而在发送OPTIONS请求后,并没有得到服务器的允许,所以,浏览器根本就没有发送正式请求,注意,得不到服务器确认,浏览器压根就不会发送正式请求,要解决这个问题,我们就需要在服务器添加对应的响应头信息

 

至此,CORS基本的原理就介绍完了;

总结一下:

出于安全性的考虑,浏览器引入同源策略;

可以使用JSONP及同域代理绕过同源策略进行跨域数据共享,但目前不再建议使用;

CORS本质上就是使用各种头信息来是浏览器与服务器之间进行身份认证实现跨域数据共享

CORS中最常使用的响应头为 Access-Control-Allow-Origin、Access-Control-Allow-Headers、Access-Control-Expose-Headers;

CORS中最常使用的请求头为 Origin、Access-Control-Request-Headers、Access-Control-Request-Method;

CORS请求分为简单请求和复杂请求(需预检请求);

代码示例:

下面列出CORS头信息共有哪些及对应的含义:

Access-Control-Allow-Origin : 指示请求的资源能共享给哪些域,可以是具体的域名或者*表示所有域。

Access-Control-Allow-Credentials : 指示当请求的凭证标记为 true 时,是否响应该请求。

Access-Control-Allow-Headers : 用在对预请求的响应中,指示实际的请求中可以使用哪些 HTTP 头。

Access-Control-Allow-Methods: 指定对预请求的响应中,哪些 HTTP 方法允许访问请求的资源。

Access-Control-Expose-Headers : 指示哪些 HTTP 头的名称能在响应中列出。

Access-Control-Max-Age : 指示预请求的结果能被缓存多久。

Access-Control-Request-Headers :用于发起一个预请求,告知服务器正式请求会使用那些 HTTP 头。

Access-Control-Request-Method : 用于发起一个预请求,告知服务器正式请求会使用哪一种 HTTP 请求方法。

Origin : 指示获取资源的请求是从什么域发起的

3.5.1 Access-Control-Allow-Origin(必选)

涉及简单Http请求、非简单Http请求

含义:允许的域名,只能填 *(通配符)或者单域名

举例

https://www.huaweicloud.com网页,发送https://portal.huaweicloud.com请求,如果服务器响应头域中没有填写Access-Control-Allow-Origin,浏览器会报错:

或者取值不为https://www.huaweicloud.com,浏览器也会报错:

填写为*或者https://www.huaweicloud.com,则不会报错:

3.5.2 Access-Control-Allow-Credentials(可选)

涉及简单Http请求、非简单Http请求

含义:表示是否允许发送Cookie,只有一个可选值:true(必为小写)。如果不包含cookies,请略去该项,而不是填写false。这一项与 XmlHttpRequest 对象当中的 withCredentials 属性应保持一致,即 withCredentials 为true时该项也为true;withCredentials 为false时,省略该项不写。反之则导致请求失败。

举例

当XmlHttpRequest中设置了withCredentials为true,如果服务器响应里没有Access-Control-Allow-Credentials字段,则浏览器会报错:

特别的,当XmlHttpRequest中设置了withCredentials为true时,还要求Access-Control-Allow-Origin字段不能为通配符*,其实这也好理解,因为设置了withCredentials,表示允许跨域发送Cookie,如果Origin允许为*的话,安全性就会大大降低了,很容易构造跨站攻击

 

3.5.4 预检请求preflight

根据上述分析,如果是非简单Http请求,浏览器会先发送一个预检请求,要求服务器进行确认。预检请求使用的方法是OPTIONS,表示这个请求是用来询问的,头信息里面,关键字段是Origin,表示请求来自哪个源。

除了Origin字段,"预检"请求的头信息包括两个特殊字段:

  • Access-Control-Request-Method

该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法,如PUT:

  • Access-Control-Request-Headers

该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段,当使用了简单请求那5个字段之外的字段,浏览器会在OPTIONS请求头域中,指定 Access-Control-Request-Headers的取值,如:

如果预检请求的响应,浏览器没有校验通过,不允许跨域,浏览器除了会在控制台报错之外,后续实际请求也不会发送了。

 

3.5.5 Access-Control-Allow-Methods(必选)

涉及非简单Http请求

含义:允许跨域请求的 http 方法(如POSTGETOPTIONS、PUT、DELETE),该字段是对于预检请求中的Access-Control-Request-Method的回复。

备注:对于简单请求的GET、POST方法,该字段不是必选的,浏览器会默认允许这两个方法进行跨域

举例

https://www.huaweicloud.com,跨域访问https://portal.huaweicloud.com,HTTP方法为POST,如果服务器响应里没有Access-Control-Allow-Methods,跨域请求能够成功:

如果服务器响应里Access-Control-Allow-Methods不包含POST,跨域请求也能成功:

如果请求为PUT方法,但响应里没有携带Access-Control-Allow-Methods或者取值不包含PUT,浏览器会报错:

3.5.6 Access-Control-Allow-Headers(可选)

涉及非简单Http请求

含义:该字段指定了跨域允许设置的非简单Http请求头(5个简单Http请求头之外的头域),(当预请求中包含 Access-Control-Request-Headers 时必须包含)– 这是对预请求当中 Access-Control-Request-Headers 的回复,和上面一样是以逗号分隔的列表,可以返回所有支持的头部。

举例

如果在XMLHttpRequest中设置了wise_groupid字段,而服务器响应中,没有Access-Control-Allow-Headers头域,或者Access-Control-Allow-Headers头域的值不包含wise_groupid,则浏览器会报错:

3.5.3 Access-Control-Expose-Headers(可选),和上面的 Allow-Headers 不同,Expose-Headers是允许浏览器获取该相应中的 headers

涉及简单Http请求、非简单Http请求

含义:CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。

举例

在JS代码中,通过XMLHttpRequest对象来获取响应中的wise_traceid头域,如:

xhr. getResponseHeader("wise_traceid")

如果在服务器响应中,没有携带Access-Control-Expose-Headers或者Access-Control-Expose-Headers的值不包含wise_traceid,则浏览器会报错,JS拿到的值也是null:

3.5.7 Access-Control-Max-Age(可选)

涉及简单Http请求、非简单Http请求

含义:用来指定本次预检请求的有效期,单位为秒。例如,Access-Control-Max-Age被设置为1728000,表示有效期是20天(1728000秒),即允许缓存该条回应1728000秒(即20天),在此期间,不用发出另一条预检请求。

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值