1. 跨域是什么?
跨域,是指浏览器不能执行其他网站的脚本。
2. 为什么会产生跨域?
因为浏览器的同源策略(Same Origin Policy),对 JavaScript 实施了安全限制。非同一域名、协议、端口的请求,是不被浏览器允许的(浏览器会将该请求返回的响应内容拦截,并给出跨域警告)。
3. 只要非同源的请求都会受限制么?
跨域的限制行为是仅存在于浏览器的。这也就是为什么会出现通过 API 请求工具调用接口的时候没有问题,但通过浏览起发起请求时就会出现跨域警告。
4. 跨域请求,浏览器会做什么?
- 请求发出
- 简单请求请求方法:GET、HEAD、POST
请求头部字段:Accept、Accept-Language、Content-Language、Content-Type(只能是这三个值之一:application/x-www-form-urlencoded、multipart/form-data、text/plain)、Last-Event-ID、不包括自定义头部字段。(好长一堆T.T,不用硬背,大概知道是这个意思就行了8⃣️)表现: 判定为简单请求的话,请求会被直接发出。 - 非简单请求
请求方式:PUT、DELETE、PATCH等
包含自定义头部字段等……
(我理解就是不满足简单请求的话,就是非简单请求)表现: 在请求发起时,浏览器为了确认服务端是否支持客户端发起非简单请求,会先发出一次预检请求(preflight request),请求方法为 OPTIONS,确认服务端允许该请求后,浏览器才会发出真实的请求。
- 响应返回在浏览器接收到响应后,会校验以下响应头中的字段,确认服务端是否允许本次跨域请求:
- Access-Control-Allow-Origin(服务端设置的允许共享资源的源): 是否包含该请求源或者设置为所有源。
- Access-Control-Allow-Methods(服务端设置的允许请求资源的方法): 是否包含本次请求方法。
- Access-Control-Allow-Headers(服务端设置的允许携带的请求头部字段): 该请求头字段是否超出了设置范围则。
- Access-Allow-Max-Age(本次预检请求的有效时长): 如果设置了且未超过有效时长,则不用重复发送预检请求。
- 满足服务器设置时,简单跨域请求返回响应数据,非简单跨域请求发送后续的真实请求(后续响应的处理和上述相同)。
- 不满足服务器设置时,简单跨域请求返回的响应数据会直接被浏览器拦截,抛出跨域错误。非简单跨域请求发送的预检请求确认服务端不允许该请求,则会忽略后续请求,不发送真实请求。
5. 如何解决跨域限制
- JSONP浏览器允许嵌入跨域资源的请求:
<script src="...">
嵌入跨域脚本<img>
标签嵌入图片<video>
、<audio>
标签嵌入媒体资源<iframe>
标签嵌入跨域资源<link rel="stylesheet" href="..." >
标签嵌入CSS- 字体跨域
res.send('callback(' + data + ')')
),这样客户端拿到响应时执行返回的 js 字符串(应该是用的 eval),就能得到跨域的响应数据。(JSONP 只是前后端约定好的一种 JSON 使用方式,且仅支持 GET 请求。) - CORS(推荐)服务端设置 Access-Control-Allow-Origin,将需要发送跨域请求的请求源设置到该字段中,便可支持跨域请求。
- 服务器代理服务器代理主要原理就是因为服务器不受同源限制,而让服务器做代理转发。
- 正向代理
正向代理就是客户端通过访问一个与它同源的服务器,而让这个服务器做代理,转发请求,拿到响应后返回给客户端。
客户端是不知道自己真实访问了哪台服务器的 - 反向代理
反向代理的关键就是地址映射,i.e. 就是我们发送请求到同源服务器上,然后服务器根据请求地址映射出另一个请求地址,再由它请求这个地址得到响应后返回给客户端。
最终被请求的服务器是不知道真实请求的是哪台客户端的
- 正向代理
- 基于 iframe 跨域(这里仅介绍 window.name + iframe 处理跨域的原理,还有其他方式没有去了解就不做介绍了,应该也是大同小异的:-D)首先什么是 window.name ? 其实就是字面意思,用来设置窗口的名字,但它有一个特点就是当窗口内容变化了,window.name 还是会保持不变。利用这一特性加上 iframe 可以嵌入跨域资源,我们可以如下实现跨域:在源A页面,手动创建一个 iframe 标签,并嵌入源B页面,这时我们虽然可以嵌入显示源B页面内容,但受同源策略限制,我们是拿不到源B页面内的资源的。
借助 window.name 的特性(load过后不会改变),将我们需要得到的数据,设置在源B页面的 window.name 中,接下来只需要将原来设置的非同源页面源B,重新设置成与源A同源的代理页面源C,就可以名正言顺的拿到刚才设置在 window.name 里的跨域资源了。