美文网首页
CORS解决跨域问题

CORS解决跨域问题

作者: 张云飞Vir | 来源:发表于2020-03-25 13:19 被阅读0次

    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.htmlhttp://example.com/app1/index.html 同源,以为都是http和域名相同
    http://Example.com:80http://example.com 同源,虽然写80端口,单实际上80是默认端口(可以省略)

    不同源的例子

    网址 说明
    http://example.com/app1https://example.com/app2 不同源,因为不同的协议: http 对比 https
    http://example.comhttp://www.example.comhttp://myapp.example.com 不同源,因为不同的主机名
    http://example.comhttp://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 预检请求。若请求满足所有下述条件,则该请求可视为“简单请求”:

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

    交互流程

    image.png

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

    示例:请求中含有
    Origin: http://foo.example
    

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

      Access-Control-Allow-Origin: *
    

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

    3.2 预检请求(preflight request)

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

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

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

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

    交互流程

    image.png

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

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

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

    示例:

    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说明了 预检请求的结果能够被缓存多久,即在多久内可以省略 预检请求 。

      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 ,就会被放在请求头中了。示例:

    X-PINGOTHER: pingpong
    Origin: http://foo.example
    

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

    Access-Control-Allow-Origin: http://foo.example
    

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

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

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

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

    var xhr = new XMLHttpRequest();
    xhr.withCredentials = true;
    

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

    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 头指定了预检请求的结果能够被缓存多久

      Access-Control-Max-Age: <delta-seconds>
    

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

    END

    相关文章

      网友评论

          本文标题:CORS解决跨域问题

          本文链接:https://www.haomeiwen.com/subject/qrezyhtx.html