CORS 跨域资源共享
Owner | 脱浩虎 |
---|---|
Last edited time | @September 18, 2023 5:37 PM |
-
思考几个问题
- 跨站一定跨域吗?
- 为什么有些POST请求会发出去两次请求?
- CORS的作用是啥?
-
同源策略
一句话总结:
同源策略(Same origin policy)是浏览器的一个重要安全策略, 用于保护用户的信息安全,防止恶意脚本的攻击
"同源"指的是"三个相同":
协议相同
域名相同
端口相同
源(origin)= 协议(scheme)+ 主机名(hostname)+ 端口号(port)
举例: www.eeo.cn
同源策略的目的:
是为了保证用户信息的安全,防止恶意的网站窃取数据
举例:本地开一个服务器去访问dynamic域名接口
如果非同源,共有三种行为受到限制:
-
无法读取非同源网页的 Cookie、IndexDB等
携带cookie如果满足的条件:
ini1. 同源时: cookie会自动读取与存储、发送 2. 同站时: 1. 都是 http 或者 https,或者不同的情况下 Secure 属性为 false (即 secure 是 true 的情况下,只有 https 请求才能携带这个 cookie) 2. path路径一致 要发送请求的路径,跟浏览器端 Cookie 的 path 属性必须一致,或者是浏览器端 Cookie 的 path 的子目录, 比如浏览器端 Cookie 的 path 为 /test,那么请求的路径必须为/test 或者/test/xxxx 等子目录才可以 3. 跨站时: 后端添加CORS响应头保证跨域请求正常,配合前端XHR.withCredentials=true即可正常发送cookie 必要时添加SameSite=none;Secure或者nginx反向代理解决cookie跨站问题 document.cookie = 'aa=bb; path=/; domain=.eeo.im; SameSite=None; Secure' 后端设置: 1. 网站开启 https 并将 Cookie 的 Secure 属性设置为 true 2. Access-Control-Allow-Origin 设置为具体的 origin,而不是 * 3. Access-Control-Allow-Credentials 设置为 true 4. SameSite 属性设置为 None sameSite属性 Strict: 阻止发送第三方cookie Lax: 阻止GET以外的请求自动带上Cookie None: 发送第三方cookie,前提是Secure=true
IndexDB:
非关系型数据库 持久化存储 异步操作 支持事务 同源限制 存储容量大
- 无法接触非同源网页的 DOM元素、属性或方法, 这是为了防止恶意网站从其他网站窃取敏感信息
- 无法向非同源地址发送 AJAX 请求,浏览器会阻止从一个源发出的请求获取来自其他源的响应(阻止跨域请求)
-
-
什么是跨域和跨站
子域名与当前域名是否属于跨域?
子域名与当前域名是否属于跨站?
Cross-Origin:
makefile
*结论: 只要 协议、域名、端口号中的任何一个不相同,就是跨域*
跨域的几个例子
bash
协议不同: [https://www.eeo.c](https://www.eeo.com/)n 与 [http://www.eeo.c](http://www.eeo.com/)n
域名不同: [https://www.eeo.c](https://www.eeo.com/)n 与 [https://www.eeo.im](https://www.eeo.im/)
端口号不同: [https://www.eeo.c](https://www.eeo.com/)n 与 [https://www.eeo.cn:3000](https://www.eeo.com:3000/)
跨域解决方案:
javascript
1.前端的JSONP
2.代理服务器
3.postMessage
4.服务端在响应头中配置CORS
5.nginx反向代理
Cross-Site
markdown
0.*结论:*
只要 *一级域名* 不同就是跨站
eTLD+1不同就是跨站
[www.taobao.com](http://www.taobao.com/) 和 [www.baidu.com](http://www.baidu.com/) 是跨站, [www.a.taobao.com](http://www.a.taobao.com/) 和 [www.b.taobao.com](http://www.b.taobao.com/) 是同站
[a.github.io](http://a.github.io) 和 [b.github.io](http://b.github.io) 是跨站
<https://juejin.cn/post/6844904095711494151>
TLD(Top Level Domain):
less
表示顶级域名,例如 .com、.org、.cn、[com.cn](http://com.cn/) 、github.io 等等
eTLD 表示有效顶级域名,注册于 Mozilla 维护的公共后缀列表(Public Suffix List)中,例如,.com、.co.uk、.github.io 等。
eTLD+1 则表示,有效顶级域名+一级域名,例如 [taobao.com](http://taobao.com/) 等
[公共后缀列表](https://publicsuffix.org/list/public_suffix_list.dat)
什么是一级域名?
TLD+1
例如:
使用场景:
ini
cookie的sameSite属性:
res.setHeader('Set-Cookie', 'name=value; SameSite=Lax;');
跨域与跨站的关系:
跨站一定跨域:
跨站说明一级域名不同,那么域名也就不同,所以跨域
跨域不一定跨站:
[www.a.taobao.com](http://www.a.taobao.com/) 和 [www.b.taobao.com](http://www.b.taobao.com/) 跨域但是同站(一级域名taobao.com相同)
preflight请求
markdown
*什么是预检请求?*
预检请求(Preflight Request)是 CORS 中的一个可选步骤, 用于验证跨域请求是否安全。
大白话总结: 非简单请求中,客户端提前打听一下服务端支持的请求选项
*预检请求的主要特征是:*
1. 请求方法是 OPTIONS 方法
2. 浏览器请求头包含 Origin、Access-Control-Request-Method 等信息
3. 服务器需要返回 Access-Control-Allow-Origin 等头信息
4. 预检请求一般不返回内容(只需要response headers)
预检请求完成后,浏览器评估服务器返回的头信息,来判断跨域 AJAX 请求是否可以发送。
*预检请求的作用:*
用于获取服务端所支持的通信选项。
简单来说,就是可以用 options 请求去嗅探某个请求在对应的服务器中都支持哪种请求方法。
*预检请求的流程:*
1. 浏览器发现跨域 AJAX 请求为复杂请求,需要进行预检。
2. 浏览器发送 OPTIONS 请求到服务器,请求头中包含:
- Origin:请求页面源信息
- Access-Control-Request-Method:实际请求将使用的 HTTP 方法
- Access-Control-Request-Headers:实际请求将包含的头信息
3. 服务器接收到预检请求,验证 Origin 并检查 HTTP 方法和头信息。
4. 服务器返回响应,响应头中包含:
- Access-Control-Allow-Origin:允许的请求源
- Access-Control-Allow-Methods:允许的 HTTP 方法
- Access-Control-Allow-Headers:允许的请求头
5. 浏览器接收到预检响应,然后发送实际的 AJAX 请求。
6. 服务器完成实际 AJAX 请求的处理,返回对应响应。
7. 浏览器接收到响应,完成页面渲染。
预检请求的类型:
ruby
简单请求
```
1. Method限制:
请求的方法是 GET、POST 及 HEAD(获取资源的元信息)
如果请求使用了其他 HTTP 方法,就不再被视为简单请求
2. Header限制:
请求的 HTTP 标头只能是以下几种常见的标头:
Accept
Accept-Language
Content-Language
Content-Type
Last-Event-ID
HTML 头部 header field 字段:
DPR、Download、Save-Data、Viewport-Width、WIdth。
如果请求使用了其他标头,同样不再被视为简单请求
3. Content-Type限制:
请求类型是 application/x-www-form-urlencoded、multipart/form-data 或 text/plain
```
复杂请求
```
1. Method限制:
PUT/DELETE/CONNECT/OPTIONS/TRACE/PATCH
2. Header限制:
简单请求外的字段
3. Content-Type限制:
application/x-www-form-urlencoded、multipart/form-data、text/plain之外
```
*CORS请求头控制字段:*
```
request header 的关键字段:
Origin
跨源请求的源站,值为源站的URL(不包含任何路径信息)
Access-Control-Request-Method
告知服务器,实际请求将使用的方式
Access-Control-Request-Headers
告知服务器,实际请求将携带的自定义请求首部字段
如:
Origin: www.eeo.cn
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type
注意:
Access-Control-Request-Method、Access-Control-Request-Headers
是在存在跨域请求,浏览器主动添加的行为
response header 的关键字段:
Access-Control-Allow-Origin:
支持的域名,如果要允许所有域名则设置为 *
Access-Control-Allow-Methods
支持的请求方式
Access-Control-Allow-Headers
支持的headers字段
Access-Control-Max-Age
预检请求缓存时间,单位为秒
在此期间,不用发出另一条预检请求
```
*总结:*
options 请求就是预检请求,可用于检测服务器允许的 http 方法。 当发起跨域请求时,由于安全原因,触发一定条件时浏览器会在正式请求之前自动先发起 OPTIONS 请求,即 CORS 预检请求,服务器若接受该跨域请求,浏览器才继续发起正式请求
-
CORS
什么是CORS?
CORS,全称是 "跨域资源共享" (Cross-origin resource sharing)
一句话总结:
通过设置 HTTP 响应头来允许浏览器向跨域服务器发送请求,从而解决 AJAX 请求跨域限制问题( 设置请求头来解决跨域问题 )
CORS产生的原因:
CORS的产生,源于浏览器的同源策略,限制了网页无法向非同源地址发送AJAX请求。 但实际上,如今很多网页应用都需要向非同源的服务器请求数据,于是为了突破这个限制,允许在服务器一侧配置一些CORS规则,来告诉浏览器允许哪些非同源的请求可以通过
CORS 的基本思想:
服务器在响应中提供一个标头(HTTP 头),指示哪些源被允许访问资源。
浏览器在发起跨域请求时会先发送一个预检请求(OPTIONS 请求)到服务器, 服务器通过设置适当的 CORS 标头来指定是否允许跨域请求,并指定允许的请求源、方法、标头等信息
CORS的作用:
-
允许浏览器向跨域服务器(协议、域名、端口任一不同即跨域)发起请求,从而克服AJAX只能同源使用的限制。
-
通过标准化的HTTP头部允许服务器声明哪些源站有权限访问资源。
一句话总结:解决跨域访问服务器资源
-
-
例子
同域名的情况:
浏览器Network展示:
请求、响应头参数:
跨域的情况:
1.浏览器 Network展示
- 请求头参数:
3.响应头参数:
-
为什么本地使用 webpack 进行 dev 开发时,不需要服务器端配置 cors 的情况下访问到线上接口?
当你在本地通过 Ajax 或其他方式请求线上接口时,由于浏览器的同源策略,会出现跨域的问题。 但是在服务器端并不会出现这个问题。
它是通过 Webpack Dev Server 来实现这个功能。 当你在浏览器中发送请求时,请求会先被 Webpack Dev Server 捕获, 然后根据你的代理规则将请求转发到目标服务器, 目标服务器返回的数据再经由 Webpack Dev Server 转发回浏览器。 这样就绕过了浏览器的同源策略限制,使你能够在本地开发环境中访问线上接口。
webpackDevServer本身是一个反向代理服务器,服务器之间不存在跨域问题。
-
为什么有些接口明明是配了CORS策略,但在接口502时,仍然会报跨域的错误?
502状态码代表 Bad Gateway,表示服务器作为网关或者代理,从上游服务器接收到无效的响应
在这种情况下,浏览器收到的响应头并不是完整的 CORS 头,不包含 Access-Control-Allow-Origin 等CORS相关头信息。因此浏览器会判断这个响应违反了CORS策略,从而报跨域错误
还有一些其他的场景:
所以:
不能看到报cors相关的error,就都当跨域处理,也有可能是接口报错了