前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >CORS解决跨域问题

CORS解决跨域问题

作者头像
张云飞Vir
发布2020-03-27 10:29:55
1.7K0
发布2020-03-27 10:29:55
举报
文章被收录于专栏:写代码和思考写代码和思考

0. 背景

浏览器中,网站A的网络请求访问网站A的资源(图片,HTTP请求)是很顺畅的,而想访问网站B的资源,就要面对跨域资源访问的问题了。面对跨域问题,有很多的解决方案,本文讨论使用 CORS 来解决的方案。

本文结构

1. 什么是跨域问题,什么是同源策略 1.1 不同源则触发一个跨域的HTTP请求 1.2 同源策略 1.3 源 2. CORS 概述 3. CORS 的控制场景 3.1 简单请求 3.2 预检请求 3.3 附带携带身份凭据的请求 3.4 响应头的额外暴露字段 3.5 预检请求的缓存时长

1. 什么是跨域问题,什么是同源策略

跨域资源共享是由同源策略引发的,首先要了解同源策略。而要了解同源策略先要了解什么是“源”,下面我们层层展开。

1.1 不同源则触发一个跨域的HTTP请求:

在浏览器中,当 “一个资源” 向 “与它所在的服务器不同的域、协议或端口” 请求一个资源时,该资源会发起一个跨域 HTTP 请求。

浏览器可能“限制发起跨域请求",或者是 “可以发起跨域请求,但是返回结果被浏览器拦截”。

出于安全原因,浏览器限制跨源HTTP请求。这意味着使用 Web应用程序只能从加载应用程序的同一个域请求HTTP资源,除非响应报文包含了正确CORS响应头。

1.2 同源策略

同源策略是一个重要的安全策略,它用于限制一个origin的文档或者它加载的脚本如何能与另一个源的资源进行交互。它能帮助阻隔恶意文档,减少可能被攻击的媒介。

也就是说,如果“源”相同,则运行访问。如果不同,则被限制。我们继续了解下什么是源。

1.3 源

Web内容的源由它的URL的 协议,主机(域名)和端口定义。

只有当协议,主机和端口都匹配时,两个对象被认为具有相同的起源。而可以使用 CORS 解除这个限制。

源由三部分组成:

  • 协议
  • 主机(域名)
  • 端口

同源的例子

网址

说明

http://example.com/app2/index.html ,http://example.com/app1/index.html

同源,以为都是http和域名相同

http://Example.com:80 ,http://example.com

同源,虽然写80端口,单实际上80是默认端口(可以省略)

不同源的例子

网址

说明

http://example.com/app1 ,https://example.com/app2

不同源,因为不同的协议: http 对比 https

http://example.com , http://www.example.com , http://myapp.example.com

不同源,因为不同的主机名

http://example.com , http://example.com:8080

不同源,因为不同的端口号。

浏览器的同源策略提升了安全性,然而在业务需求中仍然需要需要“访问不同源的资源”,于是提出了“CORS机制”。

现代浏览器支持使用 CORS,以降低跨域 HTTP 请求所带来的风险。CORS 机制允许 Web应用 进行跨域访问控制,从而使跨域数据传输得以安全进行。

2. CORS 概述

跨域资源共享 CORS 是一种机制,它使用额外的 HTTP头 来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的资源。

CORS 使用额外的请求头来说明访问是被允许的

跨域资源请求分为:

  • (1)服务器通过请求头来声明“允许的源站,和允许的资源”
  • (2)预检请求
  • (3)携带身份凭据(cookie等)的情形

跨域资源共享标准新增了一组 HTTP 请求头字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。

对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。

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

3. CORS 的控制场景

下面分几个场景来说明。

3.1 简单请求

简单请求不会触发 CORS 预检请求。若请求满足所有下述条件,则该请求可视为“简单请求”:

代码语言:javascript
复制
使用下列方法之一:
    GET
    HEAD
    POST
HTTP的头信息不超出以下几种字段:
    Accept
    Accept-Language
    Content-Language
    Content-Type 的值仅限于下列三者之一:
      text/plain
      multipart/form-data
      application/x-www-form-urlencoded

交互流程

(1)请求端: 当发起一个跨域请求时,浏览器会自动在请求头中加入 Origin 字段,它是发起方所处于的域,表明了“来源”。

代码语言:javascript
复制
示例:请求中含有
Origin: http://foo.example

(2)服务端: 服务端根据“来源” 来决定处理方式,如果同意,则返回的消息头中添加 Access-Control-Allow-Origin 字段。这个字段的值可以是“ * ”(表示任意的域名都允许),或者是具体的域名地址。

代码语言:javascript
复制
  Access-Control-Allow-Origin: *

简单请求的跨域,通过 Access-Control-Allow-Origin 请求头的处理。 如果在 请求头中 包含了特殊自定义内容,就需要 预检请求 了。

3.2 预检请求(preflight request)

“需预检的请求”要求必须首先使用 OPTIONS 方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。

"预检请求“的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。

当请求满足下述任一条件时,即应首先发送预检请求:

代码语言:javascript
复制
(1)使用了下面任一 HTTP 方法:
    PUT
    DELETE
    CONNECT
    OPTIONS
    TRACE
    PATCH
 (2)Content-Type 的值不属于下列之一:
        application/x-www-form-urlencoded
        multipart/form-data
        text/plain
 (3)请求头中包含的自定义请求头
    比如含有 Authorization, token 作为授权的字段

交互流程

示例假设: 假设我们自定义了一个 请求头字段 “X-PINGOTHER” , 后续将在请求中携带这个请求头字段。

(1) 请求端: 先发一个 OPTION 的预检请求,内容有:

Origin 说明了来源 Access-Control-Request-Method 说明 下次将正式采用的方法。Access-Control-Request-Headers 说明将采用的自定义header 字段名。

示例:

代码语言:javascript
复制
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type

(2) 服务端: 服务收到上面的请求后,根据自身情况来判定是否接收和处理。如果同意接受,则返回的 响应中包含下面几个请求头。

Access-Control-Allow-Origin 说明了支持跨域的来源 Access-Control-Allow-Methods 说明了支持的跨域方法 Access-Control-Allow-Headers 说明了 将接受的自定义header字段名 Access-Control-Max-Age说明了 预检请求的结果能够被缓存多久,即在多久内可以省略 预检请求 。

代码语言:javascript
复制
  Access-Control-Allow-Origin: http://foo.example
  Access-Control-Allow-Methods: POST, GET, OPTIONS
  Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
  Access-Control-Max-Age: 86400

至此,完成了 预检。

(3) 请求端 预检请求完成之后,发送实际请求,在这里 假设的自定义请求头字段 X-PINGOTHER ,就会被放在请求头中了。示例:

代码语言:javascript
复制
X-PINGOTHER: pingpong
Origin: http://foo.example

(4) 服务端: 服务端 根据实际情况处理请求,仍然要返回 Access-Control-Allow-Origin 声明。

代码语言:javascript
复制
Access-Control-Allow-Origin: http://foo.example

是否需要发送 预检请求,是浏览器根据规则自动做出判断。预检的过程和头部字段也是浏览器自动处理。如果在这个过程中发生了“拒绝”,那么,在发送预检请求后,就没后后续了,浏览器会 “不再发送实际的请求”,或者 “丢失实际请求中的响应”。

3.3 附带携带身份凭据的请求

对于跨域 请求,浏览器不会发送身份凭证信息。如果要发送凭证信息,需要设置 XMLHttpRequest 的某个特殊标志位。

(1)请求端 在请求端中的 withCredentials 属性则告诉浏览器“ 是否自动在请求中携带 cookie 的值 ”

代码语言:javascript
复制
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

(2)服务端 服务端的请求头中的 Access-Control-Allow-Credentials 说明了是否接受凭据信息(比如cookie)。

代码语言:javascript
复制
Access-Control-Allow-Credentials: true

如果 请求端包含了 withCredentials ,而服务端未包含 Access-Control-Allow-Credentials,那么浏览器将丢失 这次 服务端的响应内容,而不传递给请求的发送者。

附带身份凭证的请求与通配符 对于附带身份凭证的请求,服务器不得设置 Access-Control-Allow-Origin 的值为“”。 这是因为请求的首部中携带了 Cookie 信息,如果 Access-Control-Allow-Origin 的值为“”,请求将会失败

3.4 响应头的额外暴露字段

服务端通过响应头中的字段 Access-Control-Expose-Headers 来说明额外暴露字段。

CORS请求时,一般只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。

如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。

3.5 预检请求的缓存时长

Access-Control-Max-Age 头指定了预检请求的结果能够被缓存多久

代码语言:javascript
复制
  Access-Control-Max-Age: <delta-seconds>

参数 delta-seconds 表示 预检请求的结果在多少秒内有效。

END

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0. 背景
  • 1. 什么是跨域问题,什么是同源策略
    • 1.1 不同源则触发一个跨域的HTTP请求:
      • 1.2 同源策略
        • 1.3 源
        • 2. CORS 概述
        • 3. CORS 的控制场景
          • 3.1 简单请求
            • 3.2 预检请求(preflight request)
              • 3.3 附带携带身份凭据的请求
                • 3.4 响应头的额外暴露字段
                  • 3.5 预检请求的缓存时长
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档