跨域原理以及解决方法

这是一篇学习笔记,尝试将学习的内容转成笔记。

产生跨域原因 – 浏览器的同源策略

出于安全考虑,浏览器同源策略约束了不同源之交的交互,简单说就是浏览器不允许没有经过信任的源随意进行读写操作。

同源的规定

两个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的处理方式不同,满足一下条件则为简单请求:

  1. 请求方法为一下三种之一:
  • HEAD
  • GET
  • POST
  1. 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
  1. 请求中的任意XMLHttpReqeustUpload对象均没有注册任何事件监听题
  2. 请求中没有使用ReadableStream对象

CORS简单请求和响应流程

image.png

(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),预检请求校验通过,才发起正式请求,具体流程如下:

image.png

下面是一个非简单请求的例子。
web.ruqimobility.com 域下发起一个请求

http = new XMLHttpRequest()
http.open('post', 'https://client.ruqimobility.com:8443/queryCityList')
http.send()
复制代码

在控制台中可以看到有两条请求记录,一条是option请求,一条是post请求,option请求为预检请求(perflight),post请求为正式请求。

image.png

首先是预检请求,预检请求和响应头部信息如下:

image.png

预检请求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请求响应成功,相关跨域信息校验成功,发起正式请求
复制代码

预检错误信息:
image.png

预检通过发起正式请求,通过预检后发起正式请求就和简单请求一样。请求头带上Origin,响应头带上Accept-Control-Allow-Origin。

image.png

使用CORS减少option请求的方法

  1. 没有必要尽量使用简单请求
  2. 使用复杂请求可通过配置Access-Control-Max-Age减少option请求次数

CORS浏览器兼容性

image.png

可以看到主流浏览器的多数是支持的,而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);

复制代码

输出结果:
image.png

从使用方法可以看出,JSONP的原理是:在document中插入一个script标签,通过script标签去请求服务器资源,由于使用标签请求资源不受同源策略的约束,故而可以跨域请求资源。请求的资源通过请求的方法中带上的callback函数回调返回。

JSONP优缺点

  • 缺点:只能用于get请求
  • 优点:支持所有浏览器,使用简单

使用JSONP前后端的工作:

  • 前端:定义回调函数用于响应请求
  • 后台:接收到请求后,需要调用回调函数将响应内容传给前端

参考资料:

MDN跨源资源共享
阮一峰浏览器同源策略及其规避方法
阮一峰跨域资源共享 CORS 详解
卤蛋实验室 十五张动图全面解析CORS

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享