天天看点

#yyds干货盘点# 浏览器跨域请求 原理和个人理解

什么是跨域请求

我们知道,我们web的网站的网页,会展示在浏览器中,其中的按钮、或者脚本等等可以触发事件、发起http请求的。至于发送什么样的http请求,那完全就是我们开发者(网站开发者)设计的。

我们可以按照业务需要,发出任意的http请求。

任意的http请求就是千奇百怪了,可以是对自家网站后端的http请求,也可以是对其他相关网站的请求。甚至于如果我们某些开发者、黑.客不怀好意,那就发起对某些网站的恶意的请求。

但是因为是在浏览器之中,实际的所有的http请求自然是浏览器发出的。

浏览器出于安全考虑,并不是所有域名都可以直接访问。 浏览器在发起任意的请求之前,浏览器会判断目标的url是不是和当前网站是来自相同的域名, 相同则一般认为是安全的,不同则被任务是跨域,此种请求就是跨域请求,它默认是不可信、不安全的。

(当然,如果不是由浏览器发出的请求,那么自然不是浏览器的责任,比如ddos,就不属于跨域请求的范畴了)

———— 这个就是对跨域请求的最简单的理解。

#yyds干货盘点# 浏览器跨域请求 原理和个人理解

至于什么是相同域名、不同域名, 其实也很简单,这里不在赘述。

#yyds干货盘点# 浏览器跨域请求 原理和个人理解

什么是普通请求

其实并没有什么普通请求,这个是我自己对请求的分类。我们可以把非跨域请求理解为普通请求。

包括 同域请求、 从浏览器地址栏发起请求。 

同域请求 就是浏览器中某个页面发起的对自家网站后台的请求。下面谈谈从浏览器地址栏发起请求。

从浏览器地址栏发起请求

那么 浏览器从地址栏发起的任意url的请求, 和 浏览器从 页面发起的跨域的请求, 有什么区别吗?

区别肯定是有的,

浏览器从地址栏发起的任意url的请求, 不会有referer,不存在跨域问题。

所以,从浏览器地址栏发起请求 都是普通请求!

浏览器从页面发起的任意url的请求,会有referer, ( 是不是必然有呢? )

然后 目标域名的服务器会检查是不是自己的域名发起的请求

———— 这个是 web 服务器的常规操作。(是否可以禁用呢? )

如果来源是, 即referer是 自己域名的url,一般不会拒绝。如果不是, 那么可以选择拒绝以防止csrf 等攻.击,然后就不返回 任何内容? 还是返回一个 cors error 的响应头呢? 这个都是可以设置的,关键在于后端服务器。

普通请求和跨域请求 的区别

普通请求是 没有的, 而 跨域请求会多一个请求头,比如Origin: http://localhost:9999, 表示自己的 域名,( 包括 options 请求也会多 一个 Origin请求头, 如果有options 请求的话 )

这个 Origin请求头, 自然是浏览器在判断不是相同的域名之后添加的。

web 服务器就代表了其前端的资源—— 因为其对外提供的访问服务:包括页面、json 等任何资源 (包括前端浏览器 能展示的, 不能展示的 )

也都是来自于 web server端,即服务端。

一个 web server端 可以认为代表了一个域、一个域名。

浏览器可以任意方式发起一个对 任意域名 的访问, 从浏览器地址栏, 某个浏览器页面的 按钮、页面的html 源码触发,或者js 触发。

一般而言,浏览器可以在地址栏发起任意url 的请求,但是 浏览器 一般都是不允许在 页面内发起对其他域的请求。

当然, 也不是完全不行,只是默认不行。

简单请求和非简单请求

浏览器将CORS请求,即跨域请求分为两类:简单请求(simple request)和非简单请求(not-simple-request)

简单请求浏览器不会预检,而非简单请求会预检。这两种方式怎么区分?

首先搞懂什么是非简单请求, 它包括:

1 请求方式是PUT或者DELETE,

2 或者Content-Type字段类型是application/json,

3 包括其他自定义的请求头 的请求。

除去非简单请求,那么其他的就是简单请求了,一个简单的非此即彼的分类。

实际上, 在发起跨域请求的时候,浏览器会先判断是否是非简单请求,

如果是非简单请求,

那么会先发起对目标域名的 options请求,即预检请求。 如果目标域名的服务器允许了, 那么在发起正式的请求。预检请求通过了,此时发起正式请求一般都会通过吧... 此时就被禁止的可能性就不大(个人认为)。

如果预检请求不通过,比如浏览器通过预检请求响应发现,目标域名的服务器不允许当前网站发送跨域请求,或者不允许POST,那么控制台报错,不发送正式请求。

#yyds干货盘点# 浏览器跨域请求 原理和个人理解

如果浏览器判断是简单请求,

那么就无需先发起options请求了,直接发起正式请求。此时发起的正式请求,仍然很可能被服务端禁止,被禁止的话,浏览器控制台通常会抛出cors xxx之类的错误。。

总结一下就是:

简单请求:   一次: 正式请求

非简单请求:  两次:预检请求+ 正式请求;

为什么要做这样的区分?为什么要对非简单跨域请求先发起options请求呢? 大概是为了更加的安全、保险起见。我估计是因为:

1 put、delete 操作一般认为是新增、删除, 自然需要谨慎些。

2 Content-Type字段类型是application/json 那么可能是xhr 等发起的请求,xss、csrf 可能性更大。

3 包括其他自定义的请求头 的请求,同上,其可能是xhr等发起的请求,xss、csrf 可能性更大

跨域请求的细节

浏览器处理跨域请求的时候,基本是通过请求头、响应头处理的。

请求头关键是:

Origin

Access-Control-Request-Headers: content-type

Access-Control-Request-Method: POST

响应头的关键是:

Access-Control-Allow-Headers: content-type

Access-Control-Allow-Methods: GET,HEAD,POST

Access-Control-Allow-Origin: http://localhost:8080

Access-Control-Expose-Headers: X-Powered-By

继续阅读