跨域问题解决方案之CORS
文章目录
- 跨域问题解决方案之CORS
- 概述
- 浏览器的同源策略
- 同源的判定规则
- 目的
- 同源策略的限制范围
- 浏览器的同源策略为什么会引发跨域问题?
- CORS规则
- CORS解决方案
- CORS方案将请求分为两类
- 举例
- 简单请求
- 预检请求
- 总结
- 学以致用
概述
- 浏览器安全的基石是"同源政策"(same-origin policy)。
- 1995年,同源政策由 Netscape 公司引入浏览器。目前,所有浏览器都实行这个政策。
浏览器的同源策略
同源的判定规则
-
它的含义是指,A网页设置的 Cookie,B网页不能打开,除非这两个网页"同源"。所谓"同源"指的是"三个相同"。
- 协议相同
- 域名相同
- 端口相同
-
如果两个 URL 的 协议、端口(如果有指定的话)和 域名 都相同的话,则这两个 URL 是同源的。
-
下表给出了与 URL
http://store.company.com/dir/page.html
的源进行对比的示例:
URL | 结果 | 原因 |
---|---|---|
http://store.company.com/dir2/other.html | 同源 | 只有路径不同 |
http://store.company.com/dir/inner/another.html | 同源 | 只有路径不同 |
https://store.company.com/secure.html | 失败 | 协议不同 |
http://store.company.com:81/dir/etc.html | 失败 | 端口不同(http:// 默认端口是 80) |
http://news.company.com/dir/other.html | 失败 | 主机不同 |
目的
- 同源政策的目的,是为了保证用户信息的安全,防止恶意的网站窃取数据。
- 设想这样一种情况:A网站是一家银行,用户登录以后,又去浏览其他网站。如果其他网站可以读取A网站的 Cookie,会发生什么?
- 很显然,如果 Cookie 包含隐私(比如存款总额),这些信息就会泄漏。更可怕的是,Cookie 往往用来保存用户的登录状态,如果用户没有退出登录,其他网站就可以冒充用户,为所欲为。因为浏览器同时还规定,提交表单不受同源政策的限制。
- 由此可见,"同源政策"是必需的,否则 Cookie 可以共享,互联网就毫无安全可言了。
同源策略的限制范围
- 随着互联网的发展,"同源政策"越来越严格。目前,如果非同源,共有三种行为受到限制。
Cookie
、LocalStorage
和IndexDB
无法读取。DOM
无法获得。AJAX
请求不能发送。
浏览器的同源策略为什么会引发跨域问题?
- 请求发出 -> 响应 -> 浏览器会有个校验
- 当浏览器校验通过的时候,就不会有跨域的问题
- 当浏览器校验不通过的时候,就会引发跨域的问题
- 这个校验的规则就叫做CORS规则
- 所以,我们要做的就是让浏览器通过这个校验,也就没有跨域的问题了。而我们要让浏览器通过校验,就必须要了解CORS规则。
CORS规则
- CORS全称:Cross-Origin Resource Sharing,中文翻译为:跨源资源共享 或 跨域资源共享。
- CORS是一种基于 HTTP 头的机制,用于浏览器校验跨域请求。
- 它的基本理念是:
- 只要 服务器 明确表示 允许 ,则校验 通过 。
- 服务器 明确 拒绝 或 没有表示 ,则校验 不通过 。
- 从原则中,我们可以看出,要用CORS来解决跨域问题,主要靠服务器来解决,也就是后端。
- 使用CORS方案的前提,必需保证服务器是自己人。
CORS解决方案
- 跨域的解决方案有很多,但是CORS是最正统的解决方案,也是官方推荐使用的方案。
CORS方案将请求分为两类
- CORS方案将请求分为两类:
-
简单请求(Simple requests),若请求满足所有下述条件,则该请求可视为简单请求:
- 请求方法为之一:
GET
、HEAD
、POST
- 头部字段满足CORS安全规范,详见 W3C。简单来说只要我们不去改动请求的头部,就是满足安全规范的。
- 请求头的
Content-Type
为下面三个中的一个:text/plain
multipart/form-data
application/x-www-form-urlencoded
- 请求方法为之一:
-
预检请求(Preflight request / Preflighted requests):
- 只要不是简单请求的都是预检请求
- “需预检的请求”要求必须首先使用
OPTIONS
方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。
-
举例
-
举例说明哪些是简单请求,哪些是预检请求
// 简单请求: GET请求、没有改动请求头、没有改动Content-Type fetch('https://douyin.com'); // 预检请求: 改动了请求头 fetch('https://douyin.com', { headers: { a: 1, }, }); // 简单请求: POST请求、没有改动请求头、没有改动Content-Type fetch('https://douyin.com', { method: 'POST', body: JSON.stringify({a: 1, b: 2}) }); // 预检请求: 改动了Content-Type不满足第三个要求 fetch('https://douyin.com', { method: 'POST', headers: { 'content-type': 'application/json' } body: JSON.stringify({a: 1, b: 2}) })
简单请求
- 在发送请求的时候,浏览器发现跨域了,会自动带上一个请求头,这是自动的,我们不用管,我们也改不了,就是
Origin
,Origin
表达的是当前的页面源,即从哪个源发送了这个跨域请求。 - 此时服务器收到该请求发现
http://my.com
是自己人,此时服务器就要告诉浏览器你别管了这是自己人。服务器告诉浏览器这是自己人的方式有两种方案:- 第一种方案:设置一个响应头
Access-Control-Allow-Origin
并跟上这个源。只要这个源和当前的页面源保持一致,那么浏览器就知道服务器已经明确表示允许,则浏览器校验通过。 - 第二种方案:设置响应头
Access-Control-Allow-Origin
为*
,这会告诉我浏览器:服务器对所有源都通过,那么浏览器也会校验通过。
- 第一种方案:设置一个响应头
预检请求
- 当发送为非简单请求,即预检请求的时候,浏览器会引起高度重视。
所以在第一步不会真实的发生这个请求,而是先发送一个询问给服务器(浏览器会先问一下服务器:哥们,我这边现在有个页面源在跨域请求你,我担心这个动作有危险,我先问一下你行不行,如果你告诉我行,我再发真实的请求给你)。 - 所以,实际上第一步会发送一个预检请求向服务器进行询问,预检请求的请求方法为
OPTIONS
,然后会带上3个请求头参数Origin
:表达的是当前的页面源,即从哪个源发送了这个跨域请求。Access-Control-Request-Method
:用什么样的请求方法在请求。Access-Control-Request-Headers
:这次的请求改动了哪些请求头。
- 当服务器收到OPTIONS的预检请求后,如果服务器说没问题,那么服务器会返回
Access-Control-Allow-Origin
:服务器告诉浏览器我允许的源。Access-Control-Allow-Methods
:服务器告诉浏览器我允许的请求方法,可以是多个。Access-Control-Allow-Headers
:服务器告诉浏览器我允许的请求头是包含哪些的Access-Control-Max-Age
:服务器告诉浏览器只要是在86400秒内,只要是来自于Origin
内的这个源的请求你都不要再重复的问我了,我都是这一套回答,你可以把它缓存起来。
- 通过了预检请求之后,然后再进入第二部发送真实的请求,此时就和普通请求也就是简单请求是完全一样的了。
总结
- CORS方案,服务器处理的逻辑就是按照浏览器的这套规范,对相应的头部做出设置即可。
学以致用
- 领导让前端小王使用CORS在页面上自行解决跨域的问题。前端小王可以完成要求吗?
- 不能。因为CORS是要动服务器的。
- 前端小王在页面中想使用AJAX调用抖音的API接口,却发生了跨域的问题。小王想通过CORS来解决这个问题。前端小王可以解决吗?
- 不能。必须要保证服务器是自己人。
- 跨域上传图片没有问题,但是提交普通表单却遇到了跨域问题。前端小王仔细分析后,认为是服务器不支持预检请求导致的。前端小王分析的结果有可能发生吗?
- 完全有可能。
因为上传图片用的是POST请求方法,用的Content-Type
是multipart/form-data
,所以上传图片很有可能是一个简单请求。
而提交普通表单可能是一个ajax,而通常在公司里,大部分使用json格式发过去的,这就要把Content-Type
改成application/json
,这样就变成了预检请求,那么预检请求遇到跨域问题就有可能是服务器支持简单请求,不支持预检请求导致的。
- 完全有可能。