跨域
- 1. 什么是跨域?
- 2.1 简单请求
- 2.2 非简单请求
- 2.3 预检请求
1. 什么是跨域?
浏览器有一个同源策略:如果两个 url 的协议、域名、端口三者完全相同,那就称之为同源。
同源之间获取资源是不受限制的,如果不满足同源(即协议、域名、端口有一个条件不同),那么获取资源就会受到限制,此时我们称之为跨域。
总结来说:如果两个url之间需要进行通信,但是不满足同源策略,此时就发生了跨域。
下面是一个例子:(所有url场景取自新浪首页,即url_1)
/*
url_1: https://www.sina.com.cn/
url_2: https://www.sina.com.cn/api/hotword.json
url_3: https://sspapi.zenyou.71360.com/js?i=537&o=2&ran=7160123520
url_4: https://b.zenyou.71360.com/bid/zhendao
在 url_1 向 url_2 发送一个请求,此时不发生跨域
在 url_1 向 url_3 发送一个请求,此时产生跨域问题,因为 url_1 url_3 之间域名不同
在 url_1 向 url_4 发送一个请求,此时产生跨域问题,因为 url_1 url_4 之间域名不同
*/
2. HTTP 请求的理解
要了解跨域, 首先要知道HTTP 协议的两个概念: 简单请求和预检请求
2.1 简单请求
某些请求不会触发cors预检请求,这样的请求称之为简单请求,值得注意的是,该术语并不属于fetch规范;
对于简单请求,浏览器直接发出CORS请求,具体来说,就是在头信息之中,增加一个Origin字段;在返回头信息中,增加一个Access-Control-Allow-Origin: * 字段(后端不作处理时)
简单请求并不会触发跨域, 只有非简单请求才会触发跨域.
满意所有下述条件,则该请求可视为简单请求:
1. 使用下列方法
1.1 GET
1.2 HEAD
1.3 POST
2. HTTP头部只包含如下字段(这些字段统称为对 CORS 安全的首部字段集合)
2.1 Accept
2.2 Accept-Language
2.3 Content-Language
2.4 Content-Type (需要注意额外的限制)
2.5 DPR
2.6 Downlink
2.7 Save-Data
2.8 Viewport-Width
2.9 Width
3. Content-Type 的值仅限于下列三者之一:
3.1 text/plain
3.2 multipart/form-data
3.3 application/x-www-form-urlencoded
4. 请求中的任意XMLHttpRequestUpload 对象均没有注册任何事件监听器
5. 请求中没有使用 ReadableStream 对象
2.2 非简单请求
非简单请求, 就是字面上的意思; 需要注意的是, 开发过程中基本上所有的请求都是非简单请求. 在浏览器发送非简单请求时, 并不是直接发送请求到服务器, 而是会先发送一个预检请求到服务器.
当一个请求满足下列任意一个条件时,它会变成非简单请求(即该请求会先发送一个options请求到服务器)
1. 使用下列方法
1.1 PUT
1.2 DELETE
1.3 CONNECT
1.4 OPTIONS
1.5 TRACE
1.6 PATCH
2. 人为设置了“对CORS 安全的首部字段集合”之外的其他首部字段(这些字段统称为对 CORS 安全的首部字段集合)
2.1 Accept
2.2 Accept-Language
2.3 Content-Language
2.4 Content-Type (需要注意额外的限制)
2.5 DPR
2.6 Downlink
2.7 Save-Data
2.8 Viewport-Width
2.9 Width
3. Content-Type 的值不属于下列三者之一:
3.1 text/plain
3.2 multipart/form-data
3.3 application/x-www-form-urlencoded
4. 请求中的XMLHttpRequestUpload 对象注册了任意多个事件监听器
5. 请求中使用了ReadableStream对象
2.3 预检请求
预检请求, 即option 请求; 他表示当浏览器需要向服务器发送非简单请求时, 提前发送的一个请求.
预检请求的作用是确认服务器与浏览器之间是否能够进行通信, 以及为后续的请求做准备
PS: 对于预检请求, 这里只知道在跨域问题中可以用于确认一个请求是否发生了跨域, 至于在其他方面的应用, 暂时还没有接触过.
3. 跨域演示
express(app.js):
const express = require('express')
const log = console.log.bind(console)
const app = express()
app.get('/helloworld', (request, response) => {
response.send('hello')
})
const main = () => {
let server = app.listen(2300, () => {
let host = server.address().address
let port = server.address().port
log(`应用实例,访问地址为 http://${host}:${port}`)
})
}
if (require.main === module) {
main()
}
html(crossOriginDemo.html):
跨域demo
发送请求
跨域原因:
由于页面使用的端口号是63342, 而服务器 设置的端口号是2300, 这破坏了同源策略, 因此发生了跨域, 不能够正常请求到数据.
点击页面按钮之后, 可以看到跨域提示:
4. 跨域的解决方案
- cors 模块:
- json:
- webpack devServer 代理:
- 自定义node 转发:
- nginx
- postMessage
- iframe
5. 跨域demo
跨域Demo演示
6. 测试/生产环境中的跨域问题
- webpack dev Server(develop server)
- nginx
- cors
上面的这三个内容是在web 项目中常见的跨域解决方案, 但是, 如果webpack 使用webpack dev Server 解决跨域, 那么这个解决方式只能在本地运行时解决跨域问题, 如果后端在部署项目的时候没有部署webpack dev server(即没有使用在服务器建一个中转服务器, 并配置dev server), 那么还是会有跨域问题; webpack dev Server 是前端跨域解决方案, 但是不适用于生产环境和测试环境, 只适用于本机
cors 模块的配置和nginx 的配置一般是后端完成的.
因此测试/生产环境中的跨域问题, 大多数情况下都是后端去解决.
7. 参考链接
- MDN
- 跨域资源共享 CORS 详解
- 项目地址