浏览器的同源策略和跨域详解(内含故事解析)
前⾔
去年这个时候有写过⼀篇⽂章叫,写这个⽂章是因为我朋友学习前端刚好遇到了这个问题,但是就在昨天,他在学习java的时候⼜遇到同样的问题,看来我⼜要操作⼀波了。(实则我就他这⼀个朋友)(  )
提纲内容
重述⼀遍何为同源策略(因为之前讲过)
跨域的三种⽅式
剖析CORS⽅式跨域(重点)
故事解析(次重点)
何为同源策略
同源策略就是协议相同、域名相同、端⼝相同相同的⽹页才能资源互通。
简单的来说,分为三个,我访问淘宝⽹站,再访问京东⽹站,这两者资源肯定不能互通是吧。
1、也就是cookie和LocalStorage、IndexDB等⽆法互通。
2、DOM⽆法获取
3、AJAX 请求在浏览器端有跨域限制
也就是说⼏乎所有的请求都会跨域,why?主要还不是因为很多公司为了解决web访问对后台造成压⼒,都会把web服务器和后台接⼝服务器分到不同的域中,前后端不分离除外,另⼀个原因是前端开发⼈员调⽤后台测试,测试服务器在后台,web却运⾏在⾃⼰电脑上。⼀个是localhost,⼀个是对应测试服务器的域名。
跨域的三种⽅式
CORS⽅式
代理请求⽅式
剖析CORS⽅式跨域
CORS是⼀个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)
CORS可以理解为后端配置⼀些东西,主动同意哪些不同域名可以请求我的接⼝,从⽽实现跨域请求的问题,(对了,提⽰⼀下,jsonp虽然简单好⽤,但是只能实现get请求,所以⽐较单⼀。但是jsonp可以适⽤于所有浏览器,CORS对ie只适⽤于ie10及以上,对其他浏览器都是⽀持的。)
先上⼀张图(后⾯你会回来看的)
浏览器将cors请求分为两类,简单请求和⾮简单请求,阮⼀峰在⼀⽂中作了很详细的区分,满⾜⼀下两⼤条件就是简单请求,否则就是⾮简单请求。如下:
1、请求⽅法是以下三种⽅法之⼀:
* HEAD
* GET
发送ajax请求的步骤* 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、⾃定义请求头有哪些定义有哪些?
:⽤来告知(服务器)客户端可以处理的内容类型
:允许客户端声明它可以理解的⾃然语⾔,以及优先选择的区域⽅⾔
:是⼀个entity header (实体消息⾸部),⽤来说明访问者希望采⽤的语⾔或语⾔组合,这样的话⽤户就可以根据⾃⼰偏好的语⾔来定制不同的内容
实体头部⽤于指⽰资源的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字段)”,所以,试卷根本就没有发给⽼师。
注意
简单请求是只有⼀次请求的,⾮简单请求是两次请求。然后⼀切都透彻了啊!