这是一篇学习笔记,尝试将学习的内容转成笔记。
产生跨域原因 – 浏览器的同源策略
出于安全考虑,浏览器同源策略约束了不同源之交的交互,简单说就是浏览器不允许没有经过信任的源随意进行读写操作。
同源的规定
两个Url的协议,域名,端口号一致则为同源。以”juejin.cn:8080/user/210192…” 为例
结构分析:
协议 | 域名 | 端口 | 路径 |
---|---|---|---|
https | juejin.cn | 8080 | user/2101921963053144 |
与”juejin.cn:8080/user/210192…” 同源
链接 | 是否同源 | 原因 |
---|---|---|
fttp://juejin.cn:8080/user/2101921963053144 | 否 | 协议不同 |
jueyin.cn:8080/user/210192… | 否 | 域名不同 |
juejin.cn:8081/user/210192… | 否 | 端口号不同 |
juejin.cn:8080/account/210… | 是 | 协议,域名,端口号一致 |
同源策略的约束
- Cookie、LocalStorage 和 IndexDB 无法读取。
- DOM无法获得
- Ajax请求不能发送
ps:IndexDB是一种类似于数据库的数据存储,IndexDB简介
如何规避同源策略
规避方法1: CORS – 针对Ajax请求的规避同源策略绝佳方案
CORS(Cross-Origin-Resource-Sharing),全称跨源资源共享。是浏览器一种基于http headers的机制,该机制允许服务端标志除了自己以外的origin,以此让浏览器访问这些不同源的资源。
浏览器将对于CORS请求分为简单请求和非简单请求,两种请求CORS的处理方式不同,满足一下条件则为简单请求:
- 请求方法为一下三种之一:
- HEAD
- GET
- POST
- HTTP的头部信息不超过一下几种字段
- Accept
- Accept-Language
- Content-Language
- DPR
- Donwlink
- Save-Data
- Viewport-Width
- Width
- Content-Type: 限于三个值application/x-www-form-urlencoded,multipart/form-data,text/plain
- 请求中的任意XMLHttpReqeustUpload对象均没有注册任何事件监听题
- 请求中没有使用ReadableStream对象
CORS简单请求和响应流程
(1) 客服端发起请求:浏览器在识别到是CORS请求后在request headers 加上Origin字段,值为当前文档域名
(2) 服务端响应请求:服务端响应。响应头部带上Access-Control-Allow-Origin字段,值为 * 或指定的域名列表,(例如:’foo.example‘, ‘boo.example’)。
// 关键headers
// request:
Origin: http://foo.example
// response:
Access-Control-Allows-Origin: http://foo.example // 允许指定域名跨域(允许跨域白名单)
Accept-Control-Allows-Origin: * // 允许任意跨域
复制代码
简单请求的请求和响应和普通请求差不多,origin浏览器自行带上,整个流程只需要服务端配置响应头部允许跨域白名单即可。
CORS非简单请求和响应流程
对比简单请求,在发起非简单请求正式请求之前,浏览器会先发起一个预检请求(perflight),预检请求校验通过,才发起正式请求,具体流程如下:
下面是一个非简单请求的例子。
在web.ruqimobility.com 域下发起一个请求
http = new XMLHttpRequest()
http.open('post', 'https://client.ruqimobility.com:8443/queryCityList')
http.send()
复制代码
在控制台中可以看到有两条请求记录,一条是option请求,一条是post请求,option请求为预检请求(perflight),post请求为正式请求。
首先是预检请求,预检请求和响应头部信息如下:
预检请求request headers 关键header:
Origin: 当前所在域
Access-Control-Request-Method:请求方法
Access-Control-Request-Headers:自定义的header
预检请求reponse headers 关键header:
Access-Control-Allow-Credentials: true // 允许请求带上验证信息
Access-Control-Allow-Headers: content-type, device-id, device-type, platform, timestamp, version-code // 服务端允许带上的header
Access-Control-Allow-Methods: POST // 服务端允许的请求的方法
Access-Control-Allow-Origin: https://web.ruqimobility.com // 服务端允许源
Access-Control-Max-Age: 1800 // perflight验证有效时间,单位为s
复制代码
预检结果:
1. option请求响应失败,status不等于200,直接阻断正式请求
2. option请求响应成功,相关跨域信息校验错误,阻断正式请求
- Access-Control-Allow-Headers:自定义头部校验不通过
- Access-Control-Allow-Methods:请求方法校验不通过
- Access-Control-Allow-Origin:请求源校验不通过
3. option请求响应成功,相关跨域信息校验成功,发起正式请求
复制代码
预检错误信息:
预检通过发起正式请求,通过预检后发起正式请求就和简单请求一样。请求头带上Origin,响应头带上Accept-Control-Allow-Origin。
使用CORS减少option请求的方法
- 没有必要尽量使用简单请求
- 使用复杂请求可通过配置Access-Control-Max-Age减少option请求次数
CORS浏览器兼容性
可以看到主流浏览器的多数是支持的,而IE10以下是不支持的
CORS优缺点
- 优点:适用于所有请求,包括get post delete等
- 缺点:部分低版本浏览器不兼容
使用CORS前后端各自工作
- 前端:请求时带上需要验证的头,Origin浏览器会自行带上,需要前端自己带上的一般是和后台约定的自定义用于验证的头部
- 后台:响应时候,需要带上允许跨域的相对应头部信息
规避方法二:使用JOSNP
JSONP的使用和原理
使用JSONP::
const url = `https://apis.map.qq.com/ws/geocoder/v1/?location=39.984154,116.307490&key=YNCBZ&get_poi=0&callback=getCurrentLocation&output=jsonp`;
const script = document.createElement('script');
script.src = url;
window.getCurrentLocation = (data) => {
console.log('data', data)
}
document.body.appendChild(script);
复制代码
输出结果:
从使用方法可以看出,JSONP的原理是:在document中插入一个script标签,通过script标签去请求服务器资源,由于使用标签请求资源不受同源策略的约束,故而可以跨域请求资源。请求的资源通过请求的方法中带上的callback函数回调返回。
JSONP优缺点
- 缺点:只能用于get请求
- 优点:支持所有浏览器,使用简单
使用JSONP前后端的工作:
- 前端:定义回调函数用于响应请求
- 后台:接收到请求后,需要调用回调函数将响应内容传给前端
参考资料:
MDN跨源资源共享
阮一峰浏览器同源策略及其规避方法
阮一峰跨域资源共享 CORS 详解
卤蛋实验室 十五张动图全面解析CORS