浏览器的同源策略和跨域详解(内含故事解析)

  • 2019 年 10 月 17 日
  • 筆記

前言

去年这个时候有写过一篇文章叫《ajax中的json和jsonp详解》,写这个文章是因为我朋友学习前端刚好遇到了这个问题,但是就在昨天,他在学习java的时候又遇到同样的问题,看来我又要操作一波了。(实则我就他这一个朋友)(๑→ܫ←)

提纲内容
  • 重述一遍何为同源策略(因为之前讲过)
  • 跨域的三种方式
  • 剖析CORS方式跨域(重点)
  • 故事解析(次重点)

何为同源策略

同源策略就是协议相同、域名相同、端口相同相同的网页才能资源互通。
简单的来说,分为三个,我访问淘宝网站,再访问京东网站,这两者资源肯定不能互通是吧。
1、也就是cookie和LocalStorage、IndexDB等无法互通。
2、DOM无法获取
3、AJAX 请求在浏览器端有跨域限制

也就是说几乎所有的请求都会跨域,why?主要还不是因为很多公司为了解决web访问对后台造成压力,都会把web服务器和后台接口服务器分到不同的域中,前后端不分离除外,另一个原因是前端开发人员调用后台测试,测试服务器在后台,web却运行在自己电脑上。一个是localhost,一个是对应测试服务器的域名。

跨域的三种方式

剖析CORS方式跨域

CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)

CORS可以理解为后端配置一些东西,主动同意哪些不同域名可以请求我的接口,从而实现跨域请求的问题,(对了,提示一下,jsonp虽然简单好用,但是只能实现get请求,所以比较单一。但是jsonp可以适用于所有浏览器,CORS对ie只适用于ie10及以上,对其他浏览器都是支持的。)

先上一张图(后面你会回来看的)

浏览器将cors请求分为两类,简单请求和非简单请求,阮一峰在跨域资源共享 CORS 详解一文中作了很详细的区分,满足一下两大条件就是简单请求,否则就是非简单请求。如下:

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

简单请求按钮(我朋友写的就是简单请求,但是服务器并没有做cors配置),请求如下:

如第一张图的执行流程,会在请求头增加Origin字段为"http ://localhost"到后端,后端去检测Origin信息,此时检查的请求头部有没有自定义的,那么问题来了。
1、为毛要再次检查请求头?因为你前端信息为不可信信息,别人可以伪造浏览器请求加入Origin信息,并且自定义请求头。
2、自定义请求头有哪些定义有哪些?

  • Accept:用来告知(服务器)客户端可以处理的内容类型
  • Accept-Language:允许客户端声明它可以理解的自然语言,以及优先选择的区域方言
  • Content-Language:是一个 entity header (实体消息首部),用来说明访问者希望采用的语言或语言组合,这样的话用户就可以根据自己偏好的语言来定制不同的内容
  • Content-Type实体头部用于指示资源的MIME类型 media type(不属于简单请求的三个的任何一个)
  • DPR
  • Downlink
  • Save-Data
  • Viewport-Width
  • Width

他调这个接口的时候并没有自定义头部,只有设置了Content-Type为application/x-www-form-urlencoded,但是这并不影响,所以现在到了判断是否符合要求的位置,什么符合要求呢?但是当然是跨源资源共享验证,但是他后台并没有配置任何的跨域访问,所以所有的跨域请求多会被拒绝门外,但是信息是正常返回的,只不过返回头里面没有加Access-Control-Allow-Origin这个字段,如图一所示。
这是时候会发生什么情况呢?截两张图就知道了。

what?
浏览器确实收到消息,但是他就是不给你,并且告诉你,你小子跨域了,并且服务器不同意你的跨域请求,所以就直接抛一个错误给你,你自己看着办吧!

非简单请求步骤就多了一步

在不满足简单请求的情况之下(就是非简单请求),此时,如图一所示,并不直接发送请求,而是发送一个OPTIONS预检请求(所以下次看到OPTIONS请求的时候就知道这其实是一个前菜,好戏在后头呢。嘻嘻),后台也是去做一个跨源资源共享验证,他这里没有配置,当然不成功,所以还是正常返回,这次返回头里不仅不给返回Access-Control-Allow-Origin,而且不会有任何响应主体(如果你不配置的话),这也就是为啥后来他胡乱配置一通后发现post请求竟然变成OPTIONS请求了,然后不传数据还不给返回数据的原因。请求视图如下:

(细心了小伙伴会发现多了两个字段,一看就明白,不用解释了。嘻嘻)

怕他看不懂,所以这里也用一个小故事稍微解析一下子

首先来建立三个角色,小明(请求代码,也可以说是开发者)、课代表(浏览器)、老师(服务器)

  • 简单请求:
    小明(请求代码)做了一份试卷(请求包)交给课代表(浏览器),课代表分析得出,这是一份简单试卷(简单请求),然后写上这是这是一班的试卷(加上Origin字段),并且告诉老师这是一份简单试卷,老师(服务器)拿到试卷后,就开始阅读。
    首先看这个题(请求头部),是不是简单的试卷,毕竟课代表说的话不可信。
    如果不简单就直接丢了,如果是简单试卷就再看看课代表写的是几班的试卷。
    老师说我只改一班和三班的试卷(跨源资源共享验证),刚好这是一班的试卷,所以就改完直接还给课代表了,课代表发现没有异样就直接给小明了。
    如果这不巧是二班的试卷,老师还是把试卷给改出来(服务器返回正常的结果),但是同时在试卷上加上一句话,“我是一班和三班的老师,以后不要发二班的试卷给我了(返回头中没有Access-Control-Allow-Origin字段)”。
    课代表大发雷霆,不仅不把试卷(服务器正常结果)给小明,还警告小明(抛出错误)。

  • 非简答请求:
    小明(请求代码)做了一份试卷(请求包)交给课代表(浏览器),课代表分析得出,这是一份很难的试卷(非简单请求),然后课代表写一封信(OPTIONS请求,里面写上这是一班的试卷)给老师。
    老师收到这封信,老师改一班和三班的试卷,所以就批改这封信(后台设置返回的信息,不设置是没有返回主体的),ok,没问题,请把试卷给我(返回头中有Access-Control-Allow-Origin字段)。
    然后课代表发现信没有异样,后面的流程就像批改简单试卷流程一样了。
    如果,课代表发送的信里写这的是“这是二班的试卷”,那么,老师还是会批改这封信,然后正常返回这封信。关键是老师又加上那句话“我是一班和三班的老师,以后不要发二班的试卷给我了(返回头中没有Access-Control-Allow-Origin字段)”,所以,试卷根本就没有发给老师。

  • 注意
    简单请求是只有一次请求的,非简单请求是两次请求。然后一切都透彻了啊!