什么是跨域请求
我们知道,我们web的网站的网页,会展示在浏览器中,其中的按钮、或者脚本等等可以触发事件、发起http请求的。至于发送什么样的http请求,那完全就是我们开发者(网站开发者)设计的。
我们可以按照业务需要,发出任意的http请求。 任意的http请求就是千奇百怪了,可以是对自家网站后端的http请求,也可以是对其他相关网站的请求。甚至于如果我们某些开发者、黑.客不怀好意,那就发起对某些网站的恶意的请求。 但是因为是在浏览器之中,实际的所有的http请求自然是浏览器发出的。
浏览器出于安全考虑,并不是所有域名都可以直接访问。 浏览器在发起任意的请求之前,浏览器会判断目标的url是不是和当前网站是来自相同的域名, 相同则一般认为是安全的,不同则被任务是跨域,此种请求就是跨域请求,它默认是不可信、不安全的。 (当然,如果不是由浏览器发出的请求,那么自然不是浏览器的责任,比如ddos,就不属于跨域请求的范畴了) ———— 这个就是对跨域请求的最简单的理解。
至于什么是相同域名、不同域名, 其实也很简单,这里不在赘述。
什么是普通请求
其实并没有什么普通请求,这个是我自己对请求的分类。我们可以把非跨域请求理解为普通请求。
包括 同域请求、 从浏览器地址栏发起请求。
同域请求 就是浏览器中某个页面发起的对自家网站后台的请求。下面谈谈从浏览器地址栏发起请求。
从浏览器地址栏发起请求
那么 浏览器从地址栏发起的任意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,那么控制台报错,不发送正式请求。
如果浏览器判断是简单请求,
那么就无需先发起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
如果设置了.allowCredentials(true) ,那么 会多一个 Access-Control-Allow-Credentials: true
当然,整个过程其实还是有一些复杂的,本文只是做个简单介绍。更多的细节请等待我的后续文章。