之前一直以为CORS就是跨域,其实CORS是跨域的一种解决方案,聊一聊跨域,(●ˇ∀ˇ●)
浏览器的同源策略
浏览器有一个重要安全策略,叫做同源策略(Same-Origin Policy)
其中源 = 协议 + 域名 + 端口,如果两个URL的源相同,则它们是同源的,否则就是跨源/跨域。
具体表现为:当页面的源和运行时加载资源的源不一致时,浏览器会限制跨域资源访问。
对于AJAX请求,默认情况下,浏览器不允许AJAX跨域请求。

跨域解决方案
代理
通常在开发阶段,前端会通过代理服务器解决跨域问题
比如使用dev-server,它会在本地启动一个代理服务器,将请求转发到目标服务器,从而解决跨域问题。
CORS
跨域资源共享(Cross-Origin Resource Sharing, CORS)是一种跨域解决方案
核心理念:如果服务器允许跨域访问,那浏览器就放行这个访问,因此,CORS主要通过在服务端正确设置响应头来实现
CORS规定了三种不同的交互模式,三种模式自上而下要求越来越严格:
- 简单请求
- 要求:
- 请求方法属于:GET | POST | HEAD
- 请求头仅包含安全字段:Accept | Accept-Language | Content-Type... 不包含自定义请求头
- 请求头Content-Type仅包含:application/x-www-form-urlencoded | multipart/form-data | text/plain 注意没有
application/json
- 交互规范:
- JS发起请求
- 浏览器在请求头自动添加
Origin字段,表示请求的源 - 如果服务器允许该请求跨域访问,需要在服务器响应头中包含
Access-Control-Allow-Origin字段,表示允许的源,其值可以是*或具体源 - 浏览器收到响应后,JS正常执行后续代码逻辑
- 需预检的请求
要求:非简单请求
交互流程:
JS发起请求
浏览器发送预检请求
OPTIONS,询问服务器是否允许该请求,OPTIONS请求头包含以下:Origin:请求的源Access-Control-Request-Method:真实请求的方法Access-Control-Request-Headers:真实请求的头
如果服务器不允许,浏览器会抛出CORS错误
服务器允许的话,返回响应头包含以下:
Access-Control-Allow-Origin字段,表示允许的源, 不能是*!Access-Control-Allow-Methods字段,表示允许的请求方法Access-Control-Allow-Headers字段,表示允许的请求头Access-Control-Max-Age字段,表示允许的请求最大时间, 时间内不需要再次发送预检请求、
浏览器发送真实请求,请求头还是带着
Origin字段服务器正常处理请求,返回响应头包含
Access-Control-Allow-Origin字段,表示允许的源浏览器收到响应后,JS正常执行后续代码逻辑,
- 附带Credentials的请求
要求:非简单请求,且需要携带Credentials
交互流程:
JS必须设置 credentials: 'include',发起请求
jsfetch('http://localhost:3000/api/user', { credentials: 'include', headers: { 'Content-Type': 'application/json' } })浏览器发送预检请求
OPTIONS,还是那几个服务器允许的话,返回响应头包含以下,还是那几个,加上
Access-Control-Allow-Credentials: true浏览器发送真实请求,请求头还是带着
Origin字段,并携带Cookie浏览器收到响应后,JS正常执行后续代码逻辑
服务端的 CORS 配置,以Nodejs为例
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*')
res.header('Access-Control-Allow-Method', 'GET, POST, PUT, DELETE, OPTIONS')
res.header('Access-Control-Allow-Headers',
'Content-Type, Authorization, Content-Length, X-Requested-With');
// 3600s 内不用再预检了
res.header('Access-Control-Max-Age', '3600')
if (req.method == 'OPTIONS') {
console.log('option ! !!')
res.sendStatus(200)
return
}
next()
})JSONP
JSONP(JSON with Padding)是一种古老的跨域解决方案。了解就行
核心理念:利用script标签的src属性不受同源策略限制,可以请求不同源的资源
实现流程:
- 前端定义一个全局回调函数:function callback(data)
- 创建一个
<script>标签,src 设置为http://目标域名/api?callback=callback - 浏览器向目标服务器发起请求
- 服务器接收到请求后,返回一段 JS 脚本:
callback({ "name": "Alice" }) - 浏览器执行这段脚本,相当于调用了 callback 函数,拿到数据并执行后续逻辑
思考:
- 什么是跨域?
- 解决跨域问题有哪些手段?
- CORS的简单请求和需预检的请求如何区分?
- 说说CORS简单请求和需预检的请求在浏览器服务器交互流程?