前言

从“跨域”这个词开始,去理清跨域这个知识点,路径同源策略,跨过document.domain,window.postMessage,JSONP,CORS等,先放若干个问题,心愿看完文章的你能够答上来。

  1. 能说说跨域吗?
  2. 能说说同源策略吗?
  3. 为什么要同源策略,它限度了什么?
  4. 你晓得哪些跨域计划呢?
  5. 无关cookie的跨域怎么实现?
  6. 能具体说说JSONP吗?返回什么数据呢,前端怎么解决呢?晓得什么原理吗?实现过吗?JSONP服务器端实现过吗?
  7. postMessage 理解吗?怎么应用?须要留神什么?(平安方面)
  8. 代理理解过吗?用过哪些代理计划呢不?怎么在我的项目中用呢?
  9. cors能够具体说一个简略申请和非简略申请吗?具体过程说一下?我的项目中怎么应用?

文章可能有些中央写的不当和不全的中央,欢送评论区给我倡议,感激~~

也心愿外面的知识点有哪里不分明的,你能够本人能够花工夫去整明确更好,加油呀

这次就不放导图啦,左边目录很分明~~

1、讲一下跨域是什么?

一个源加载的文档或者脚本和来自另一个源的文档和脚本等资源进行交互(也就是不满足同源策略的两个源之间进行一些交互),就是跨域。

所以你须要分明的是同源策略是什么?它为什么呈现?它又限度了什么? 往下看吧:

2、同源策略

2.1、 同源策略是什么?

所谓"同源"指的是"三个雷同"。

  • 协定雷同
  • 域名雷同
  • 端口雷同

举个栗子:

http://www.jingda.com/dir/page.html这个网址,协定是http://,域名是www.jingda.com,端口是80(默认端口能够省略)。来看看上面改编的哪些是同源哪些是不同源:

  • http://www.jingda.com/dir2/other.html:同源
  • http://jingda1.com/dir/other.html:不同源(域名不同)
  • http://v2.www.jingda.com/dir/other.html:不同源(域名不同)
  • http://www.jingda.com:81/dir/other.html:不同源(端口不同)
  • https://www.jingda.com/dir/page.html:不同源(协定不同)

2.2、 为什么须要同源策略?

同源政策的目标,是为了保障用户信息的平安,避免歹意的网站窃取数据。它能帮忙阻隔歹意文档,缩小可能被攻打的媒介。 假如小明同学在A银行的官网进行了登录,之后他又去浏览了其余网站,如果其余网站能够读取A银行官网的cookie,那么小明在A银行的登录信息和其余贷款记录等都会被泄露,将是一件十分危险的状况。

而cookie的拜访限度只是同源策略限度的一种状况,上面咱们介绍一下其余的。

2.3、 同源策略带来了什么拜访限度?

  • 跨源数据存储拜访:拜访存储在浏览器中的数据,如 localStorage 和 IndexedDB,是以源进行宰割;Cookies 应用不同的源定义形式。每个源都领有本人独自的存储空间,一个源中的 JavaScript 脚本不能对属于其它源的数据进行读写操作。
  • 跨源脚本API拜访:JavaScript 的 API 中,如 iframe.contentWindow、 window.parent、window.open 和 window.opener 容许文档间间接互相援用。当两个文档的源不同时,这些援用形式将对 Window 和 Location对象的拜访增加限度,
  • 跨源网络拜访:同源策略管制不同源之间的交互,例如在应用XMLHttpRequest 或 [图片上传中...(image-d026b6-1618640180825-0)]

     标签时则会受到同源策略的束缚。

3、解决跨域的几种办法?

将下面三种拜访限度简化成上面的三种表白:

(1) Cookie、LocalStorage 和 IndexDB 无奈读取。

(2) JavaScript 的 API 中的一些援用,无奈取得。(详见上)

(3) AJAX 申请不能发送。(也就是无奈应用XMLHttpRequest)

(因为在网上无关跨域的解决方案,可能是比拟多,但这里我是依据下面三种限度顺次介绍一下可能行得通的解决方案)

3.1、 cookie -- document.domain

当咱们尝试解决因同源策略下,无法访问cookie这种状况时,咱们能够借助:

  • 1、浏览器容许通过设置document.domain共享 Cookie。来达成成果。然而,两个网页一级域名雷同,只是二级域名不同才能够设置,那什么是一级域名,什么是二级域名呢?

举个栗子: A网页:http://w1.jingda.com/a.html 在这个网页地址中,w1.jingda.com这部分统称为域名,

  • 一级域名是由一个非法的字符串+域名后缀组成,所以,jingda.com这种模式的域名才是一级域名,jingda是域名主体,.com、.net也是域名后缀。
  • 二级域名理论就是一级域名上面的主机名,顾名思义,它是在一级域名后面加上一个字符串,比方w1.jingda.com,

解释完怎么的状况能够设置document.domain共享 Cookie,让咱们看看一个如何操作:

假如有两个网页地址,咱们能够看到,他们的一级域名是雷同的,二级域名的不同的:

A网页:http://w1.jingda.com/a.html

B网页:http://w2.jingda.com/b.html

那么只有设置雷同的document.domain,两个网页就能够共享Cookie。

document.domain = 'example.com';复制代码

A网页通过脚本设置一个 Cookie。

document.cookie = "test1=hello";复制代码

B网页就能够读到这个 Cookie。

var allCookie = document.cookie;复制代码

2、服务器也能够在设置Cookie的时候,指定Cookie的所属域名为一级域名,比方.example.com。

Set-Cookie: key=value; domain=.example.com; path=/复制代码

这样的话,二级域名和三级域名不必做任何设置,都能够读取这个Cookie。

这里的话,补充一下设置cookie的时候,一些其余的设置来限定其可拜访性:

  1. Domain 和 Path 标识定义了Cookie的作用域:即容许 Cookie 应该发送给哪些URL。
  2. Secure:Secure属性是说如果一个cookie被设置了Secure=true,那么这个cookie只能用https协定发送给服务器,用http协定是不发送的。
  3. HttpOnly :应用 HttpOnly 属性可避免通过 JavaScript 拜访 cookie 值
  4. SameSite Cookie 容许服务器要求某个 cookie 在跨站申请时不会被发送,从而能够阻止跨站申请伪造攻打(CSRF)。

你应该留神到,这里咱们只是单单解决了在有一些限度条件下的拜访cookie的限度。然而下面还提到的LocalStorage 和 IndexDB临时还没有解决。(等下再说)

3.2、 API拜访 -- window.postMessage

postMessage是html5新增的一个解决跨域的一个办法,为了能让不同源中文档进行交换,能够应用 window.postMessage平安地实现跨源通信。(平安是指在正确的应用状况下),这

3.2.1、window.postMessage的应用场景?

这个我本人也是没有用过的。应用办法大家能够参考这篇window.postMessage用法一个比拟小的案例

因为是两个窗口页面之间的通信,因而咱们这边假如我两个页面,A,B,目标是在B窗口中点击postMessage按钮,可能在A页面收到发来的音讯。 A页面:

<script>  function test() {    let op = window.open('b.html', '_blank');     function receiveMessage(event) {      console.log('event', event);    }    op.addEventListener("message", receiveMessage, false);   }</script><body>  <div>    <button onClick="test()">open</button>  </div></body>复制代码

B页面:

<script>  function post() {    window.postMessage("hi there!", location.origin);  }  function receiveMessage(event) {    console.log('event', event)  }  window.addEventListener("message", receiveMessage, false);</script><body>  <div>    <button onClick="post()">postMessage</button>  </div></body>复制代码

我就间接说一下大略的思路了: 首先看看B页面:

  1. 在B页面有一个按钮,点击这个按钮会触发一个办法,post()
  2. 在post()办法中,window.postMessage("hi there!", location.origin),发送到所有同源的窗口,留神,以后窗口也会收到
  3. 之后通过 window.addEventListener("message", receiveMessage, false)去监听,如果有数据,就执行receiveMessage(),把数据打印进去

再来看A页面:

  1. 在A页面也有一个按钮,当点击这个按钮时触发test()
  2. 关上新窗口,并建设窗口的援用变量op = window.open('B.html', '_blank');
  3. op.addEventListener("message", receiveMessage, false); 监听新开窗口发来的音讯,通过 receiveMessage() 把数据打印进去

3.2.2、如何正确的应用,以保障安全性?

  1. 始终应用origin和source属性验证发件人的身份,没有验证origin和source属性会导致跨站点脚本攻打。
  2. 当应用postMessage将数据发送到其余窗口时,指定准确的指标origin,而不是*

3.3、JSONP

JSONP(JSON with Padding)是JSON的一种“应用模式”,可用于解决支流浏览器的跨域数据拜访的问题。

3.3.1、JSONP的介绍

JSONP 是通过在<script></srcipt>标签里,通过src,img,href 属性的跨域形式向一个不同源的网站地址发送http申请,并且使得json数据能够在javascript代码中可能应用。

它躲避了javascript代码中的跨源网络拜访,也就是无奈应用XMLHttpRequest,fetch被同源机制管到了(如果不同源的话)。

提前准备一个接口:https://photo.sina.cn/aj/index?page=1&cate=recommend 间接网页中关上,咱们是能够看到有很多数据的,如下图:

让咱们尝试在本地申请一下这个地址,看看能不能拿到数据:因为单方地址并不是同源的,因而这样申请会报跨域的错:

<body>   <script>        fetch('https://photo.sina.cn/aj/index?page=1&cate=recommend')            .then(data=>{                console.log(data);            })    </script></body>复制代码

通过 live-server关上浏览器,在控制台能够看到报错了,因为这个是一个跨域的申请:

接下来咱们来看看JSONP如何解决这个问题:

3.3.2、jsonp 如何应用?原理是什么?返回数据格式?前端怎么解决?

还是申请下面的这个网站地址,咱们把代码改成上面这样:

<body>  <script>    function callback(data){        console.log(data);    }</script><script src="https://photo.sina.cn/aj/index?page=1&cate=recommend&callback=callback"></script>  </body>复制代码

再来看看页面控制台输入:

data胜利取到了。然而咱们的数据达到之后是json数据,不能间接应用,script标签是一个加载资源的标签,它并不能间接运行这个代码。

事实上咱们是在拜访的时候,在申请的地址前面加上一个,&callback=callback,告诉服务器,本地想进行一个跨资源拜访(以JSOP的模式进行跨域)。等号前面的callback是一个你本人定义的函数,名字可自取,这个函数就是,告诉我须要申请的地址,这边页面上我有一个函数,它会期待调用,用来执行你发过来的数据(也就是能够去执行把数据申请下来的操作)。

因而在数据达到之后,还包了一层函数 callback({data}),当数据通过script标签申请下来之后,再通过callback实现了一个调用本地资源的能力。

最初再理一下这部分的内容:

  • JSONP的原理

script标签申请数据,在申请的地址前面加上一个,&callback=callback,申请的服务器就在json数据外面包一层callback函数,当这个带有数据的callback函数能够在script失去之后能够运行的函数:

  • 返回的数据格式

JSON

  • 以及前端如何解决的

JSON with padding --- callback({data})

3.3.3、本人封装一个jsonp?

  1. 筹备工作
 <script>    let jsonp = () => {    }    jsonp('https://photo.sina.cn/aj/index', {      page: 1,      cate: 'recommend'    })    .then(response => {      console.log(response,'调用胜利啦');    })  </script>复制代码
  1. 具体实现流程
  • 确定传递参数: url 、携带的参数 、callback;
  • 解决url上的参数(?前面的);
  • 筹备好url(携带callback函数);
  • 构建script标签;
  • 把这个标签挂到window上
<script>    // 1、确定好参数    let jsonp = (url,data = {},callback = 'callback') => {    // 2、解决好url外面的参数      let dataStr = url.indexOf('?') === -1 ? '?':'&'    // 3、把参数和&拼接下来      for (let key in data) {        dataStr += `${key}=${data[key]}&`;      }    // 4、把callback拼接上      dataStr += 'callback=' + callback;    // 5、创立一个script标签      let oScript = document.createElement('script');      oScript.src = url + dataStr;      document.body.appendChild(oScript);     // 6、把script标签挂载到window下来    //计划一、      // window[callback] = (data) => {      //   console.log(data);      // }    // 计划二、      return new Promise((reslove,reject) => {        window[callback] = (data) => {          try {            reslove(data)          } catch(e) {            reject(e)          } finally {            oScript.parentNode.removeChild(oScript);            //删除这个script节点          }        }      })    }    //调用jsonp办法    jsonp('https://photo.sina.cn/aj/index?a=1', {      page: 1,      cate: 'recommend'    })    .then(response => {      console.log(response,'调用胜利啦');    })  </script>复制代码

3.3.4、实现一个jsonp服务器端?(node版本,express版本)

node版本

创立一个构造如下的服务器端文件夹,咱们将在index.js中实现咱们的JSONP:

var http = require('http');http.createServer(function(req, res){// req url  callback=?console.log(req.url);let data = {a: 1};res.writeHead(200, {'Content-type' : 'text/json'})  const reg = /callback=([\w]+)/  if (reg.test(req.url)) {    let padding = RegExp.$1    res.end(`${padding}(${JSON.stringify(data)})`)  } else {    res.end(JSON.stringify(data));  }//  res.end('<p>Hello World</p>'); res.end(JSON.stringify(data));}).listen(3000);复制代码

express 版本

var express = require('express');var cors = require('cors');//后端cors 中间件const app = express();app.use(cors());app.get('/product',(req,res)=>{    res.json({        a:1,        b:2    })})app.listen(8000,()=>{    console.log('server is ok')})复制代码

3.4 cors

3.4.1、介绍一下cors?

CORS是一个W3C规范,全称是"跨域资源共享"(Cross-origin resource sharing)。它容许浏览器向跨源服务器,收回XMLHttpRequest申请,从而克服了AJAX只能同源应用的限度。

3.4.2、简略申请和非简略申请?

浏览器将CORS申请分成两类:简略申请(simple request)和非简略申请(not-so-simple request)。

除了简略申请其余的都是非简略申请,因而只有记住哪些是简略申请就能够啦:

简略申请:(须要同时满足上面两种条件)

  1. 申请办法是以下三种办法之一:
  • HEAD
  • GET
  • POST
  1. HTTP的头信息不超出以下几种字段:
  • Accept:设置承受的内容类型(申请头)
  • Accept-Language:设置承受的语言(申请头)
  • Content-Language:为关闭内容设置自然语言或者指标用户语言(响应头)
  • Content-Type:(设置申请体的MIME类型(实用POST和PUT申请))只限于三个值

application/x-www-form-urlencoded

中默认的encType,form表单数据被编码为key/value格局发送到服务器(表单默认的提交数据的格局)

multipart/form-data:将表单的数据处理为一条音讯,以标签为单元,用分隔符离开。既能够上传键值对,也能够上传文件。

text/plain:text/plain :纯文本格式

3.4.3、我的项目中怎么应用?

  • 服务器端:
const express = require('express');const app= express();app.get('/', (req, res)=>{  console.log('server is OK');  res.end('jingjing')});// app.use((req, res, next) => {//     res.header("Access-Control-Allow-Origin",'http://localhost:5500');//     res.header("Access-Control-Allow-Credentials", true);//     res.header("Access-Control-Allow-Headers", 'Content-Type,Content-Length,Authorization, Accept,X-Requested-With');//     res.header("Access-Control-Allow-Methods", 'PUT,POST,GET,DELETE,OPTIONS,HEAD');//     req.method === 'OPTIONS' ? res.send('CURRENT SERVICES SUPPORT CROSS DOMAIN REQUESTS!') : next();// });app.listen(8081, ()=>{    console.log('Server is running at http://localhost:8081')})复制代码
  • 前端申请:
  <body>    <button onclick="sendAjax()">sendAjax</button>  <script type="text/javascript">    var sendAjax = () => {        var xhr = new XMLHttpRequest();        xhr.open('GET', 'http://127.0.0.1:5500', true);        xhr.send();        xhr.onreadystatechange = function (e) {          if (xhr.readyState == 4 && xhr.status == 200) {            console.log(xhr.responseText);            console.log('胜利了')          }        };    }  </script>  </body>复制代码

跨域报错:

把两头正文的局部放开再执行:没有下面的报错了,也返回了

console.log(xhr.responseText);console.log('胜利了')

剖析一下:

  • "Access-Control-Allow-Origin",http://localhost:5500

如果服务端仅容许来自 http://localhost:5500的拜访,如果服务端返回的 Access-Control-Allow-Origin: * 表明,该资源能够被任意外域拜访。

  • "Access-Control-Allow-Credentials", true):

Access-Control-Allow-Credentials 头指定了当浏览器的credentials设置为true时是否容许浏览器读取response的内容。

  • "Access-Control-Allow-Headers", 'Content-Type,Content-Length,Authorization, Accept,X-Requested-With'):

首部字段 Access-Control-Allow-Headers 表明服务器容许申请中携带字段 X-PINGOTHER 与 Content-Type。

  • "Access-Control-Allow-Methods", 'PUT,POST,GET,DELETE,OPTIONS,HEAD':

首部字段 Access-Control-Allow-Methods 表明服务器容许客户端应用 POST, GET 和 OPTIONS 等办法发动申请

  • req.method === 'OPTIONS' ? res.send('CURRENT SERVICES SUPPORT CROSS DOMAIN REQUESTS!') : next():

“需预检的申请”要求必须首先应用 OPTIONS 办法发动一个预检申请到服务器,以获知服务器是否容许该理论申请(除简略申请以外的,比方 POST办法就须要用到预检)

3.5、代理 (nginx)

3.5.1 原理

A网站向B网站申请1.js文件时,向B网站发送一个获取的申请,nginx依据配置文件接管这个申请,代替A网站向B网站来申请这个资源,nginx拿到这个资源后再返回给a网站,以此来解决了跨域问题。

3.5.2 应用

应用Nginx,无关下载和配置Nginx,我就不再这里说了,感兴趣的小伙伴能够参考一下这篇文章,外面配置相干的讲的比较清楚。正确的Nginx跨域配置

(本人平时也没怎么用就是,唉,大多知识点也是一边写一边理)

然而的然而,学习还是要学滴。回到最开始咱们提到的一些问题,来看看你能答复多少

总结

最初再来一次拷问:

  1. 能说说跨域吗?
  2. 能说说同源策略吗?
  3. 为什么要同源策略,它限度了什么?
  4. 你晓得哪些跨域计划呢?
  5. 无关cookie的跨域怎么实现?
  6. 能具体说说JSONP吗?返回什么数据呢,前端怎么解决呢?晓得什么原理吗?实现过吗?JSONP服务器端实现过吗?
  7. postMessage 理解吗?怎么应用?须要留神什么?(平安方面)
  8. 代理理解过吗?用过哪些代理计划呢不?怎么在我的项目中用呢?
  9. cors能够具体说一个简略申请和非简略申请吗?具体过程说一下?我的项目中怎么应用?

最初

因为有拷问题了,拷问题大家能答复上也是把握啦,这次的面试题小编就以PDF模式展现给大家,不然篇幅被限度了,面试题出不来啦,须要前端面试题完整版PDF的,点击这里间接支付:

逐点冲破,冲破自我!咱们下期见!