Cors 跨域基本原理

为什么有了跨域这个东西世上本没有路,走的人多了也就有了路 。跨域这算是这么一回事 。在 Web 的世界上本没有跨域这个东西,但架不住坏人越来越多,所以后来就有了跨域 。
何出此言?
互联网刚出现的时候,是默认全开放的 。也就是你说,你写的一个网站默认是允许所有人访问的 。这一点非常符合互联网"互联互通"的精神本质 。但后来人们却发现,事情好像有点变质变味 。
比如说,Bob 写了一个网站让 Alice 访问,当 Alice 访问这个网站的时候,谁知 Eve 中途拦截了请求,在里面植入了 Eve 自己写的 Js 脚本 。这个脚本会源源不断的请求 Bob 网站的数据,并且通过浏览器发给 Eve 。

Cors 跨域基本原理

文章插图
 
Eve 的脚本此时此刻充当了小偷的角色,盗取数据并返回给 Eve 。
既然第三方的 Js 有这样的危险,那么我们对这些 Js 脚本禁止执行怎么样? 必须不能! 你想想互联网中除了有坏人,也有好人呀 。如果我们禁止所有 Js 脚本运行的话,那我们就无法使用网银了,电商平台了,甚至连绚丽多彩的 Web UI 都无法看到了 。
既然不能禁止,也不能放任自流,那又该何去何从?所以引出了跨域,跨域简而言之,就是浏览器在发请求的时候,先询问一下服务器,你让我能做点啥?
为什么是浏览器这是一个好主意,为什么跨域只出现在浏览器呢? 这也算是当前一个无解的事情,因为 Http 请求天生是无状态的,服务器无法区分这个请求和上个请求是否是同一个客户端发起 。当无法判断是否为同一个客户端时,服务端也就无法判断这个请求是应该通过还是应该禁止 。
所以跨域这件事只能下沉到浏览器来执行了 。让浏览器承担第一道防火墙的责任 。毕竟能做浏览器的都是大厂,这点基本职业操守,浏览器厂商还是有的 。
那话说回来,服务端就一点也不做么? 并不是的,服务端限制来自服务端请求有另外方案,比如 Token、API Check 等方式 。
因为 RestFul API 之间无状态,所以只能通过校验请求合法性,来变相控制对方是否有权限获取/变更数据 。
浏览器如何实现跨域浏览器通过协议+域名+端口这三者来判断是否属于跨域 。整理如下:
浏览器地址
准备访问地址
是否跨域
原因
http://www.bob.com
http://www.aclice.com
跨域
域名不一致
http://www.bob.com
https://www.aclice.com
跨域
协议、域名不一致
http://www.bob.com
https://www.bob.com
跨域
协议不一致
http://www.bob.com
http://www.bob.com:8080
跨域
端口不一致
http://www.bob.com
http://www.bob.com
不跨域
 
浏览器每次在发起请求的时候,都会经过这个逻辑判断 。如果判断需要跨域,那么就会先向服务端发起OPTIONS请求 。OPTIONS请求是向服务器询问,你允许哪些外部域名访问你?如果服务端允许当前浏览器访问,浏览器再继续访问 。如果不允许,那就此作罢 。
Cors 跨域基本原理

文章插图
 
如图所示,浏览器通过 OPTIONS 请求询问服务端 。服务端回复只接受来自https://www.bob.com的请求 。
如果当前浏览器是https://www.bob.com那么愉快的发起正式请求,如果浏览器不是https://www.bob.com 。那么就此别过以后再见 。
是不是感觉跨域好像很简单,也没有多么复杂 。事实也是这样的,跨域本身不复杂,复杂的里面的参数配置 。
常见的参数配置前面说过了,跨域的控制权限在服务端,不在客户端 。因此服务端如果需要支持跨域,那么需要将以下参数通过 OPTIONS 请求返回给客户端 。
  • Access-Control-Allow-Origin: <URI>| *
用来标识谁可以访问我 。这个 header 只支持返回一个值,要么是特定外部 URI(https://www.devexp.cn),要么是"*" 。不接受其他值 如果服务端指定了具体的域名而非“*”,那么响应首部中的 Vary 字段的值必须包含 Origin 。这将告诉客户端:服务器对不同的源站返回不同的内容 。


推荐阅读