XHR 跨域概念学习

2018-04-21  本文已影响0人  打不shi的流云

跨域访问 (Cross-origin)

最近在做一个 vue 前后分离的项目,利用 axios 处理 XHR 信息时,浏览器端报错:


image

该错误由于前端vue和后端django所属资源域不同而产生(具体原因此不详述),由此接触到跨域访问,索性做一些了解。

简单定义

浏览器的同源策略会导致跨域,这里同源策略又分为以下两种

  1. DOM同源策略:禁止对不同源页面DOM进行操作。这里主要场景是iframe跨域的情况,不同域名的iframe是限制互相访问的。
  2. XmlHttpRequest同源策略:禁止使用XHR对象向不同源的服务器地址发起HTTP请求。

只要 ++协议、域名、端口++ 之中任意一个不同,都会被当成不同的域,之间的请求被认为是跨域请求。

为什么禁止跨域

AJAX同源策略主要用来防止CSRF攻击。如果没有AJAX同源策略,相当危险。

  1. 用户登录了自己的银行页面 http://mybank.comhttp://mybank.com向用户的cookie中添加用户标识。
  2. 用户浏览了恶意页面 http://evil.com。执行了页面中的恶意AJAX请求代码。
  3. http://evil.comhttp://mybank.com发起AJAX HTTP请求,请求会默认把http://mybank.com对应cookie也同时发送过去。
  4. 银行页面从发送的cookie中提取用户标识,验证用户无误,response中返回请求数据。此时数据就泄露了。
  5. 而且由于Ajax在后台执行,用户无法感知这一过程。


    image

跨域资源共享 CORS (Cross-origin resource sharing)

但是总有那么一些场景,需要XHR跨域进行访问。CORS由此而生。

基本原理

浏览器将CORS请求分为简单请求(simple request)和非简单请求(not-so-simple request)
满足以下2个条件,即为简单请求:

  1. 请求方法是 [HEAD, GET, POST] 之一
  2. HTTP 头信息不超过这几种字段:
    1. Accept
    2. Accept-Language
    3. Content-Language
    4. Last-Event-ID
    5. Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

基本的,对于简单请求,CORS会在请求头加一个字段;而对于非简单请求,CORS会通过与请求的方式进行解决。

简单请求跨域

基本流程

对于简单请求,浏览器会在请求头信息中,加入一个Origin字段。该字段说明本请求来自于哪个源。服务器将根据这个值,判断是否同意此次请求。

GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

如果服务器拒绝,则会返回一个错误,如本文开头的那种。
如果服务器同意请求,Response的头信息中会多加入几个字段

Access-Control-Allow-Origin: http://api.bob.com # 必须字段。 要么是请求时Origin的值,要么是一个 *
Access-Control-Allow-Credentials: true # 可选字段。表示是否允许发送Cookie
Access-Control-Expose-Headers: FooBar  # 可选字段。CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段::
                                       # Cache-Control、Content-Language、Content-Type、
                                       # Expires、Last-Modified、Pragma
                                       # 如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。上面的例子指定,getResponseHeader('FooBar')可以返回FooBar字段的值。
Content-Type: text/html; charset=utf-8

withCredentials 属性

如果要把Cookie发送到服务器,一方面要服务器同意,另一方面,请求必须打开 withCredentials 属性。如

var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://example.com/', true);
xhr.withCredentials = true;
xhr.send(null);

需要注意,如果要发送cookie,Access-Control-Allow-Origin就不能设为星号,必须指定明确的Origin。

非简单请求跨域

非简单请求会在正式请求之前,增加一次http请求,以保证跨域安全。

预请求

此次请求浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。
预请求头实例:

OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT  # 列出CORS请求会用到哪些方法
Access-Control-Request-Headers: X-Custom-Header #  指定CORS会额外发送的header字段
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

预请求方法是OPTION,关键字段 Origin用于表明身份。
详查 阮一峰-CORS跨域

预请求回应

服务器检查了 OriginAccess-Control-Request-MethodAccess-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应。

  1. 如果服务器同意请求,会在response的header中加入 Access-Control-Allow-Origin 字段,该字段值可以是 * ,表示同意此次跨域
  2. 如果服务器拒绝请求,会返回一个正常HTTP回应,但是不带 Access-Control-Allow-Origin 字段。此时,浏览器会认为服务器拒绝了跨域请求,并进行错误流程。

References:

  1. 什么是JS跨域访问?
  2. 跨域资源共享 CORS 详解
  3. CORS 跨域资源共享
  4. 理解 CORS (Cross-Origin Resource Sharing)
上一篇下一篇

猜你喜欢

热点阅读