> 文档中心 > js跨域相关面试题

js跨域相关面试题

目录

一、什么是跨域

二、同源策略会造成以下的影响

三、跨域解决方案

四、跨域方法详解

1、处理跨域方法一 JSONP

2. 处理跨域方法二 CORS

3、处理跨域方法三——WebSocket

4、处理跨域方法四——postMessage

5、处理跨域方法五vue-cli代理转发


一、什么是跨域

广义的跨域包括:

  • 资源跳转:超链接跳转、重定向、表单提交
  • 资源嵌入:link、ifram、script、img,以及css样式中的background:url()、@font-face()等外链接
  • 脚本请求:js的ajax请求、js或DOM 中的跨域操作

狭义的跨域:指浏览器同源策略限制的请求

同源策略是一个重要的安全策略,它用于限制一个origin的文档或者它加载的脚本如何能与另一个源的资源进行交互。它能帮助阻隔恶意文档,减少可能被攻击的媒介。

        当协议、子域名、主域名、端口号中任意一个不相同时,都算作不同域。不同域之间相互请求资源,就算作“跨域”。

在这里插入图片描述

有一点必须要注意:跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。之所以会跨域,是因为受到了同源策略的限制,同源策略要求源相同才能正常进行通信,即协议、域名、端口号都完全一致。

二、同源策略会造成以下的影响

  • Cookie、LocalStorage 和 IndexDB 无法读取

  • DOM 和 JS 对象无法获取

  • Ajax请求发送不出去

三、跨域解决方案

跨域的方法很多,网上案例很多 常见的大概有9种,分别是

  1. jsonp跨域
  2. document.domain + iframe
  3. window.name + iframe
  4. location.hash + ifram
  5. 跨域资源共享(CORS)
  6. WebSocket协议跨域
  7. HTML5的postMessage跨域
  8. nginx代理跨域
  9. nodejs中间件代理跨域

或者也可以这么分类

对最主要的AJAX跨域来说(也就是平常调接口时):

1)(后端)服务器配置CORS(跨域资源共享)

2)(后端)node.js或nginx,反向代理,把跨域改造成同域

3)(前端)将JSON升级成JSONP,在JSON的基础上,利用标签可以跨域的特性,加上头设置

4)对iframe跨域来说:H5提供了postMessage()的方法,可以在父子页面进行通信(加分项)

在日常工作中,原生js我们使用最多的还是jsonpCORS两种

四、跨域方法详解

1、处理跨域方法一 JSONP

1.JSONP原理

核心思想:浏览器的script、img、iframe标签是不受同源策略限制的 ,所以通过script标签引入一个js文件,这个js文件载入成功后会执行我们在url参数中指定的callback函数,并把把我们需要的json数据作为参数传入。在服务器端,当req.params参数中带有callback属性时,则把数据作为callback的参数执行,并拼接成一个字符串后返回。 

2.JSONP和AJAX对比

JSONP和AJAX相同,都是客户端向服务器端发送请求,从服务器端获取数据的方式。但AJAX属于同源策略,JSONP属于非同源策略(跨域请求)

3.JSONP优缺点

- 优点:兼容性好,在很古老的浏览器中也可以用,简单易用,支持浏览器与服务器双向通信。 
- 缺点:只支持GET请求,且只支持跨域HTTP请求这种情况(不支持HTTPS

4.JSONP的代码实现

    var script = document.createElement('script');    script.type = 'text/javascript';    // 传参一个回调函数名给后端,方便后端返回时执行这个在前端定义的回调函数    script.src = 'http://www.domain2.com:8080/login?user=admin&callback=handleCallback';    document.head.appendChild(script);    // 回调执行函数    function handleCallback(res) { alert(JSON.stringify(res));    } 

后端node代码实现:

router.get('/article-list', (req, res) => {  let data = {    message: 'success!',    name: req.query.name,    age: req.query.age  }  const fn = req.query.callback  data = JSON.stringify(data)  res.end(fn + '(' + data + ')');}

2. 处理跨域方法二 CORS

上面jsonp的方式已经暴露了一个巨大的缺点,那就是无法解决post请求的跨域。因此,我们更多的是使用cors来解决跨域问题,它是 W3C 标准,属于跨源 AJAX 请求的根本解决方法,实际上就是在响应头添加允许跨域的源(意思就是允许去哪些源地址去请求这个服务器)

核心思想:在服务器端通过检查请求头部的origin,从而决定请求应该成功还是失败。具体的方法是在服务端设置Response Header响应头中的Access-Control-Allow-Origin为对应的域名,实现了CORS(跨域资源共享),这里出于在安全性方面的考虑就是尽量不要用 *,但对于一些不重要的数据则随意,例如图片。

前端什么也不用做
后端需要开启cors
 

3、处理跨域方法三——WebSocket

Websocket是HTML5的一个持久化的协议,它实现了浏览器与服务器的全双工通信,同时也是跨域的一种解决方案。WebSocket和HTTP都是应用层协议,都基于 TCP 协议。但是 WebSocket 是一种双向通信协议,在建立连接之后,WebSocket 的 server 与 client 都能主动向对方发送或接收数据。同时,WebSocket 在建立连接时需要借助 HTTP 协议,连接建立好了之后 client 与 server 之间的双向通信就与 HTTP 无关了。

原生WebSocket API使用起来不太方便,我们使用Socket.io,它很好地封装了webSocket接口,提供了更简单、灵活的接口,也对不支持webSocket的浏览器提供了向下兼容。

4、处理跨域方法四——postMessage

如果两个网页不同源,就无法拿到对方的DOM。典型的例子是iframe窗口和window.open方法打开的窗口,它们与父窗口无法通信。HTML5为了解决这个问题,引入了一个全新的API:跨文档通信 API(Cross-document messaging)。这个API为window对象新增了一个window.postMessage方法,允许跨窗口通信,不论这两个窗口是否同源。postMessage方法的第一个参数是具体的信息内容,第二个参数是接收消息的窗口的源(origin),即"协议 + 域名 + 端口"。也可以设为*,表示不限制域名,向所有窗口发送。

 

5、处理跨域方法五vue-cli代理转发

浏览器是禁止跨域的,但是服务端不禁止,在本地运行npm run dev等命令时实际上是用node运行了一个服务器,因此proxyTable实际上是将请求发给自己的服务器,再由服务器转发给后台服务器,做了亦曾代理,因为不会出现跨域问题。

如果后端jsonp也不弄, cors也不弄, 就给你个接口地址

我们可以在本地弄个服务器, 然后用服务器请求后台服务器接口地址

但是vuecli脚手架, 启动了一个webpack开发服务器, 它就能做代理转发

而且前端和这个服务器是同源的都是8080端口
需要修改webpack开发服务器的配置即可

在前端服务和后端接口服务之间 架设一个中间代理服务,它的地址保持和前端服务一致,那么:

代理服务和前端服务之间由于协议域名端口三者统一不存在跨域问题,可以直接发送请求

代理服务和后端服务之间由于并不经过浏览器没有同源策略的限制,可以直接发送请求

这样,我们就可以通过中间这台服务器做接口转发,在开发环境下解决跨域问题,看起来好像挺复杂,其实vue-cli已经为我们内置了该技术,我们只需要按照要求配置一下即可。
 

devServer: {    proxy: {      // http://c.m.163.com/nc/article/headline/T1348647853363/0-40.html      '/api': { // 请求相对路径以/api开头的, 才会走这里的配置 target: 'http://c.m.163.com', // 后台接口域名 我们要代理的真实的接口地址 changeOrigin: true, // 改变请求来源(欺骗后台你的请求是从http://c.m.163.com) pathRewrite: {   '^/api': '' // 因为真实路径中并没有/api这段, 所以要去掉这段才能拼接正确地址转发请求 }      }    }  }

 axios请求的代码

axios({    url: '/api/nc/article/headline/T1348647853363/0-40.html'})