CORS-跨域资源共享

跨域资源共享CORS学习笔记

1、同源政策

含义

1995年,同源政策由 Netscape 公司引入浏览器。目前,所有浏览器都实行这个政策。

最初,它的含义是指,A网页设置的 Cookie,B网页不能打开,除非这两个网页”同源”。所谓”同源”指的是”三个相同”。

协议相同
域名相同
端口相同
 
 
  • 1
  • 2
  • 3

举例来说,http://www.example.com/dir/page.html这个网址,协议是http://,域名是www.example.com,端口是80(默认端口可以省略)。它的同源情况如下。

http://www.example.com/dir2/other.html:同源
http://example.com/dir/other.html:不同源(域名不同)
http://v2.www.example.com/dir/other.html:不同源(域名不同)
http://www.example.com:81/dir/other.html:不同源(端口不同)
 
 
  • 1
  • 2
  • 3
  • 4

目的

同源政策的目的,是为了保证用户信息的安全,防止恶意的网站窃取数据。

设想这样一种情况:A网站是一家银行,用户登录以后,又去浏览其他网站。如果其他网站可以读取A网站的 Cookie,会发生什么?

很显然,如果 Cookie 包含隐私(比如存款总额),这些信息就会泄漏。更可怕的是,Cookie 往往用来保存用户的登录状态,如果用户没有退出登录,其他网站就可以冒充用户,为所欲为。因为浏览器同时还规定,提交表单不受同源政策的限制。

由此可见,”同源政策”是必需的,否则 Cookie 可以共享,互联网就毫无安全可言了。

限制范围

随着互联网的发展,”同源政策”越来越严格。目前,如果非同源,共有三种行为受到限制。

(1) Cookie、LocalStorage 和 IndexDB 无法读取。
(2) DOM 无法获得。
(3) AJAX 请求不能发送。
 
 
  • 1
  • 2
  • 3

对于跨域问题,可以简单理解为:浏览器执行javascript脚本时,会检查这个脚本属于哪个页面,如果不是同源页面,就不会被执行。

对于它通常有三种解决办法:

  1. JSONP:JSONP只支持GET请求
  2. 代理:例如www.123.com/index.html需要调用www.456.com/server.php,可以写一个接口www.123.com/server.cgi,由这个接口在后端去调用www.456.com/server.php并拿到返回值,然后再返回给index.html,这就是一个代理的模式。相当于绕过了浏览器端,自然就不存在跨域问题。
  3. SERVER端支持CORS,即增加对Origin等字段判断,返回响应是增加Access-Control-Allow-Origin、Access-Control-Allow-Method等字段

代理方法比较清晰,是直接通过先访问同源后端接口,通过后端接口去访问跨域资源,也就不会存在跨域的问题(跨域的问题仅存在于,浏览器执行javascript脚本时)

但代理模式也有比较明显的缺点,比如如果浏览器访问的页面需要访问的域名的资源是不断变化,那么使用代理模式就要频繁改动代码,扩展性很差,所以并不是一个好的选择。

JSONP:jsonp(json width padding)是JSON的一种“使用模式”,可用于解决主流浏览器的跨域数据访问的问题。由于同源策略,一般来说位于service.example.com的网页无法与不是service2.example.com的服务器沟通,而HTML的script元素是一个列外。利用script圆度开放策略,网页可以等到从其他源动态产生的JSON资料,而这种使用模式就是所谓的JSONP。用JSONP抓到的资料并不是JSON,而是任意的Javascript,用javascript直译器执行而不是用JSON解析器解析。

但JSONP有几个问题,一是要求客户必须限制使用JSONP,这个方案并不是后端SERVER支持跨域;二则是JSONP只支持GET方法。

JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。

这里总结下上面的内容:如果用户使用不支持跨域的APIGW作为中间件连接后端server,那么是无法通过浏览器访问后端server的,基本就代表着使用qcloud APIGW完成的代码,无法使用浏览器。。包括桌面浏览器、移动浏览器以及APP内置使用的webview均无法使用

CORS与JSONP的使用目的相同,但是比JSONP更强大。这里着重介绍下CORS跨域资源共享

2、跨域资源共享

定义

跨来源资源共享(Cross-Origin Resource Sharing(CORS))是一种使用额外HTTP标头来让目前浏览网站的user agent能获得访问不同来源(网域)服务器特定资源之权限的机制。当user agent请求一个不是目前文件来源——来自于不同网域(domain)、通信协定(protocol)或通信端口(port)的资源时,会建立一个跨来源HTTP请求(cross-origin HTTP request)。

基于安全性考虑,浏览器和WebView发出的HTTP请求会有限制。例如,XMLHttpRequest及Fetch皆遵守同源政策(same-origin policy)。这代表网络应用程序所使用的这些API只能请求来自和应用程序相同网域的HTTP资源,除非使用了CORS标头。

哪些请求会使用到CORS?

  1. 使用XMLHttpRequest或Fetch API进行跨站请求,如前所述。
  2. 网页字体(跨网域CSS的@font-face的字体用途),所以服务器可以部属TrueType字体并限制仅让被许可的网站进行跨站加载使用。
    WebGL纹理。
  3. 以drawImage绘制到Canvas画布上的图形/视频之影格。
  4. CSS样式表(让CSSOM存取)。
  5. 指令码(for unmuted exceptions)

跨域请求详解

CORS需要浏览器和服务器同时支持。当前桌面和移动浏览器对CORS的支持情况如下:

a、浏览器端支持情况

桌面浏览器:

浏览器ChromeEdgeFireFoxIEOperaSafari
支持CORS最低版本4123.510124

- IE8 和 IE9 通过 XDomainRequest 插件支持CORS,IE10 开始则完全正常支持CORS。
- Firefox 3.5 支持跨域 XMLHttpRequests 与 Web Fonts,较旧版本上某些请求会有限制。 Firefox 7 支持 WebGL 纹理的跨域 HTTP 请求,而 Firefox 9 新增支持使用 drawImage 方法,将图形绘制于 canvas 中。

移动浏览器:

浏览器Android webviewChrome for AndroidEdge mobileFirefox for AndroidIE mobileOpera AndroidiOS Safari
支持CORS最低版本2.1ALLALL4ALL123.2

整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源通信没有差别,代码完全一样。浏览器一旦发现HTTP请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求(复杂请求),但用户不会感知。

支持CORS的主要改动点在server端

b、两种跨域请求

浏览器将CORS请求分成两类:简单请求(simple request)和预检请求。

同时满足以下条件,那么就是简单请求。

1) 请求方法是以下三种方法之一:
HEAD
GET
POST
(2)HTTP的头信息不超出以下几种字段:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
(3)没有事件监听器被注册到任何用来发出请求的 XMLHttpRequestUpload 上(经由 XMLHttpRequest.upload 方法取得)上。
(4)请求中没有 ReadableStream 类型的内容被用于上传。
 
 
  • PS:虽然这些都是网页目前已经可以送出的跨站请求,除非后端服务器回复正确CORS标志,否则不会有内容传回来,因此不允许跨域请求的网站无须担心会受到新的HTTP 存取控制的影响。

    通常情况下主要涉及条件(1)和条件(2)

    如果不满足上述条件任何一个,那么它就是预检请求。

    c、简单请求

    浏览器发现自己发送的是简单跨域请求,则会只发送一次HTTP请求。相较于同源请求,CORS简单请求会在头信息中额外增加一个Origin字段。

    下图是一个简单跨域请求例子:浏览器发现本次请求是跨域请求,就会自动在请求头信息中增加Origin字段(依赖于浏览器机制/或者JS解释器的实现)
    image

    请求和响应内容如下:

    假定是从apigw.qcloud.com去请求qcloud.com的资源

    请求
    GET /resources/public-data/ HTTP/1.1
    Host: qcloud.com
    User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    Accept-Language: en-us,en;q=0.5
    Accept-Encoding: gzip,deflate
    Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
    Connection: keep-alive
    Origin: http://apigw.qcloud.com
    
    
    响应
    HTTP/1.1 200 OK
    Date: Mon, 01 Dec 2008 00:23:53 GMT
    Server: Apache/2.0.61 
    Access-Control-Allow-Origin: *
    Keep-Alive: timeout=2, max=100
    Connection: Keep-Alive
    Transfer-Encoding: chunked
    Content-Type: application/xml
       
       

HTTP请求中的Origin字段表示该请求是来自于http://apigw.qcloud.com的请求

如果Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段。

Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: application/xml
 
 

    本次请求示例中响应是携带了Access-Control-Allow-Origin: *,或者是Access-Control-Allow-Origin: http://apigw.qcloud.com

    如果Origin指定的源,不在许可范围内,服务器会返回一个正常的HTTP回应。浏览器发现,这个回应的头信息没有包含Access-Control-Allow-Origin字段,就知道出错了,从而抛出一个错误,被XMLHttpRequest的onerror回调函数捕获。注意,这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200。

    d、预检请求

    不满足简单请求条件之一的即是非简单请求。非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为”预检”请求(preflight)。

    「预检(preflighted)」请求会先用HTTP 的OPTIONS 方法请求另一个域名资源,确认后续实际(actual)请求能否可安全送出。由于跨域请求可能会携带使用者的信息,所以要先进行预检请求。

    下图是一个预检请求例子:

    image

    请求和响应内容如下:

    假定是从apigw.qcloud.com去请求qcloud.com的资源

    第一次是预检请求/响应:

    OPTIONS /resources/post-here/ HTTP/1.1
    Host: qcloud.com
    User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    Accept-Language: en-us,en;q=0.5
    Accept-Encoding: gzip,deflate
    Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
    Connection: keep-alive
    Origin: http://apigw.qcloud.com
    Access-Control-Request-Method: POST
    Access-Control-Request-Headers: X-PINGOTHER, Content-Type
    
    
    HTTP/1.1 200 OK
    Date: Mon, 01 Dec 2008 01:15:39 GMT
    Server: Apache/2.0.61 (Unix)
    Access-Control-Allow-Origin: http://apigw.qcloud.com
    Access-Control-Allow-Methods: POST, GET, OPTIONS
    Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
    Access-Control-Max-Age: 86400
    Vary: Accept-Encoding, Origin
    Content-Encoding: gzip
    Content-Length: 0
    Keep-Alive: timeout=2, max=100
    Connection: Keep-Alive
    Content-Type: text/plain
     
     

      等到预检请求完成后,浏览器才会发送真正的响应:

      POST /resources/post-here/ HTTP/1.1
      Host: qcloud.com
      User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
      Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
      Accept-Language: en-us,en;q=0.5
      Accept-Encoding: gzip,deflate
      Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
      Connection: keep-alive
      X-PINGOTHER: pingpong
      Content-Type: text/xml; charset=UTF-8
      Content-Length: 55
      Origin: http://apigw.qcloud.com
      Pragma: no-cache
      Cache-Control: no-cache
      
      <?xml version="1.0"?><person><name>Arun</name></person>
      
      
      HTTP/1.1 200 OK
      Date: Mon, 01 Dec 2008 01:15:40 GMT
      Server: Apache/2.0.61 (Unix)
      Access-Control-Allow-Origin: http://apigw.qcloud.com
      Vary: Accept-Encoding, Origin
      Content-Encoding: gzip
      Content-Length: 235
      Keep-Alive: timeout=2, max=99
      Connection: Keep-Alive
      Content-Type: text/plain
      
      [Some GZIP'd payload]
       
       

        先看请求,Access-Control-Request-Method告诉服务器发的请求是POST请求,Access-Control-Request-Headers通知自己带有X-PINGOTHER自定义header

        再看响应,Access-Control-Allow-Origin这个与前面类似,Access-Control-Allow-Methods这里说明支持POST/GET/OPTIONS方法,Access-Control-Allow-Headers这里说明允许X-PINGOTHER自定义header,Access-Control-Max-Age用来指定本次预检请求的有效时间,86400是24小时也就是一天。

        问题:1、若简单跨域请求校验失败,APIGW应如何回复?
        2、若预检跨域请求校验失败,APIGW应如何回复?
        3、目前浏览器并不支持预检请求的重定向,如果发生了预检请求的重定向,则浏览器会大概率报错

        一旦服务器通过了”预检”请求,在Access-Control-Max-Age指定的时间内,以后每次浏览器正常的CORS请求,就都跟简单请求一样,会有一个Origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段。

        e、HTTP跨域请求标识

        Origin

        Origin 字段表示了跨域请求的来源或者预检请求的来源。在任何跨域请求中,一定要携带Origin字段

        Origin: <origin>
         
         

          Access-Control-Request-Method(仅在预检请求中)

          Access-Control-Request-Method 是用在预检请求中,告诉后端server实际请求用的HTTP方法

          Access-Control-Request-Method: <method>
           
           

            Access-Control-Request-Headers(仅在预检请求中)

            Access-Control-Request-Headers标识用于预检请求中,它会告诉后端server自己所携带的自定义header字段有哪些

            Access-Control-Request-Headers: <field-name>[, <field-name>]*
             
             
              f、HTTP跨域响应标识

              Access-Control-Allow-Origin

              跨域响应会携带该字段,若服务器允许所有uri来访问自己的资源,那么则该字段为*;若要允许http://www.qq.com访问该资源,则为Access-Control-Allow-Origin: http://www.qq.com

              Access-Control-Allow-Origin: <origin> | *
               
               

                Access-Control-Expose-Headers

                Access-Control-Expose-Headers表示服务器允许浏览器从响应中解析哪些header字段

                Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header
                 
                 

                  表示服务器允许浏览器从响应中解析X-My-Custom-Header, X-Another-Custom-Header字段

                  Access-Control-Max-Age

                  Access-Control-Max-Age表示预检请求结果请求成功后,多长时间内非简单请求可以不需要再发预检请求,可以继续直接使用跨域请求请求资源

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

                    Access-Control-Allow-Credentials

                    这里下次补充。

                    Access-Control-Allow-Credentials: true
                     
                     

                    Access-Control-Allow-Methods(仅在预检请求响应中)

                    Access-Control-Allow-Methods表示服务器访问操作该资源允许哪些方法

                    Access-Control-Allow-Methods: <method>[, <method>]*
                     
                     

                      Access-Control-Allow-Headers(仅在预检请求响应)

                      Access-Control-Allow-Headers表示在访问这个域资源的时候,预检请求响应中哪些header字段可以在跨域请求中使用

                      Access-Control-Allow-Headers: <field-name>[, <field-name>]*
                       
                       

                        2、后端server实现CORS功能可以参考如下代码

                        KONG CORS模块:https://github.com/Kong/kong/tree/master/kong/plugins/cors

                        GO http middlerware:https://github.com/rs/cors

                        3、参考文档

                        http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html

                        http://www.ruanyifeng.com/blog/2016/04/cors.html

                        https://developer.mozilla.org/zh-TW/docs/Web/HTTP/CORS#Preflighted_requests

                        https://blog.csdn.net/qq_15437667/article/details/78841335

                        评论
                        添加红包

                        请填写红包祝福语或标题

                        红包个数最小为10个

                        红包金额最低5元

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

                        抵扣说明:

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

                        余额充值