1. 跨域是什么?

跨域,是指浏览器不能执行其他网站的脚本。

2. 为什么会产生跨域?

因为浏览器的同源策略(Same Origin Policy),对 JavaScript 实施了安全限制。非同一域名、协议、端口的请求,是不被浏览器允许的(浏览器会将该请求返回的响应内容拦截,并给出跨域警告)。

3. 只要非同源的请求都会受限制么?

跨域的限制行为是仅存在于浏览器的。这也就是为什么会出现通过 API 请求工具调用接口的时候没有问题,但通过浏览起发起请求时就会出现跨域警告。

4. 跨域请求,浏览器会做什么?

  • 请求发出
  1. 简单请求请求方法: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⃣️)表现: 判定为简单请求的话,请求会被直接发出
  2. 非简单请求
    请求方式:PUT、DELETE、PATCH等
    包含自定义头部字段等……
    (我理解就是不满足简单请求的话,就是非简单请求)表现: 在请求发起时,浏览器为了确认服务端是否支持客户端发起非简单请求,会先发出一次预检请求(preflight request),请求方法为 OPTIONS,确认服务端允许该请求后,浏览器才会发出真实的请求
  • 响应返回在浏览器接收到响应后,会校验以下响应头中的字段,确认服务端是否允许本次跨域请求:
    1. Access-Control-Allow-Origin(服务端设置的允许共享资源的源): 是否包含该请求源或者设置为所有源。
    2. Access-Control-Allow-Methods(服务端设置的允许请求资源的方法): 是否包含本次请求方法。
    3. Access-Control-Allow-Headers(服务端设置的允许携带的请求头部字段): 该请求头字段是否超出了设置范围则。
    4. Access-Allow-Max-Age(本次预检请求的有效时长): 如果设置了且未超过有效时长,则不用重复发送预检请求。
    表现:
    • 满足服务器设置时,简单跨域请求返回响应数据,非简单跨域请求发送后续的真实请求(后续响应的处理和上述相同)。
    • 不满足服务器设置时,简单跨域请求返回的响应数据会直接被浏览器拦截,抛出跨域错误。非简单跨域请求发送的预检请求确认服务端不允许该请求,则会忽略后续请求,不发送真实请求。

5. 如何解决跨域限制

  1. JSONP浏览器允许嵌入跨域资源的请求:
    1. <script src="..."> 嵌入跨域脚本
    2. <img> 标签嵌入图片
    3. <video><audio> 标签嵌入媒体资源
    4. <iframe> 标签嵌入跨域资源
    5. <link rel="stylesheet" href="..." > 标签嵌入CSS
    6. 字体跨域
    JSOP 就是根据 script 标签可以嵌入跨域脚本这一特性,在 script 标签里填入跨域资源 url,比较关键的一点是 url 末尾会带一个callback(回调函数),用于接收返回的跨域资源。具体一点就是客户端 callback 传给服务端,普通响应返回的都是 JSON 字符串,但如果是 JSONP 的话,服务端返回响应时会返回一串可执行的javascript 字符串。通常是返回 执行约定好的 callback 的代码字符串,并且将响应数据作为参数传入,e.x. res.send('callback(' + data + ')') ),这样客户端拿到响应时执行返回的 js 字符串(应该是用的 eval),就能得到跨域的响应数据。(JSONP 只是前后端约定好的一种 JSON 使用方式,且仅支持 GET 请求。)
  2. CORS(推荐)服务端设置 Access-Control-Allow-Origin,将需要发送跨域请求的请求源设置到该字段中,便可支持跨域请求。
  3. 服务器代理服务器代理主要原理就是因为服务器不受同源限制,而让服务器做代理转发。
    • 正向代理
      正向代理就是客户端通过访问一个与它同源的服务器,而让这个服务器做代理,转发请求,拿到响应后返回给客户端。
      客户端是不知道自己真实访问了哪台服务器的
    • 反向代理
      反向代理的关键就是地址映射,i.e. 就是我们发送请求到同源服务器上,然后服务器根据请求地址映射出另一个请求地址,再由它请求这个地址得到响应后返回给客户端。
      最终被请求的服务器是不知道真实请求的是哪台客户端的
  4. 基于 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 里的跨域资源了。