前端面试题 - 71. 如何处理跨域

155 阅读3分钟

跨域是指在同一浏览器中,当一个域的文档或脚本试图去请求另一个域下的资源时所出现的限制。这个限制是由浏览器的同源策略(Same-Origin Policy)引起的。

同源策略

浏览器的同源策略限制了当前域名与其他域名之间进行某些操作,例如读写 Cookie、LocalStorage、IndexDB等,但并非所有的域名都被认为是跨域。

下面是同源策略判断是否跨域的规则:

  • 协议相同
  • 域名相同
  • 端口号相同

若以上三者有任意一项不同,则被视为跨域,需要使用其他技术手段来实现跨域访问。

举个例子,假设我们网站的域名为 https://example.com,以下请求都视为同源:

  • https://example.com
  • https://www.example.com
  • https://example.com:8080

而以下请求都属于跨域:

  • http://example.com
  • https://api.example.com
  • https://example.com:8888

在以上跨域请求中,若需要跨域获取数据或调用 API,则可以使用 JSONP、CORS、代理等技术手段。

处理策略

  1. JSONP:利用script标签的跨域特性,通过回调函数将数据返回。
  2. CORS:在服务端设置响应头,允许指定域名进行跨域访问。
  3. 代理:通过服务端代理访问,即客户端发送请求到自己的服务器上,自己的服务器再去请求目标服务器,并将结果返回给客户端。
  4. postMessage:可以在两个窗口间互相通信,从而实现跨域。

需要注意的是,以上方法均有各自的使用场景和限制条件,开发者需要根据具体情况选择最适合的方案,同时还要保证数据的安全性。

举例

  1. JSONP

比如我们需要从一个不同域的服务器获取数据,此时可以使用JSONP,例如:

<script>
function handleData(data) {
  console.log(data);
}

const script = document.createElement('script');
script.src = 'http://example.com/data?callback=handleData';
document.body.appendChild(script);
</script>
  1. CORS

假设我们的前端应用部署在http://localhost:8080上,而后端API服务部署在http://api.example.com上,若要实现跨域访问,可以在API服务中设置允许访问的域名,例如:

// Express.js示例代码
app.use(function(req, res, next) {
  res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8080');
  next();
});
  1. 代理

假设我们需要从另一个域名的API获取数据,但该API没有启用CORS或JSONP,我们可以使用自己的服务器作为代理服务器来访问该API,例如:

// Node.js示例代码
const express = require('express');
const request = require('request');

const app = express();

app.use('/api', function(req, res) {
  const url = 'http://api.example.com' + req.url;
  req.pipe(request(url)).pipe(res);
});

app.listen(3000);

这样,我们就可以通过访问http://localhost:3000/api/xxx来获取该API的数据了。

  1. postMessage

假设我们需要在两个跨域的窗口之间传递数据,可以使用postMessage方法,例如:

// 窗口A
const targetWindow = window.opener || window.parent;
targetWindow.postMessage('hello', 'http://example.com');

// 窗口B
window.addEventListener('message', function(event) {
  if (event.origin === 'http://localhost:8080') {
    console.log(event.data);
  }
});

上述代码中,窗口A向窗口B发送消息,并指定目标窗口为http://example.com,窗口B监听message事件,当接收到来自http://example.com的消息时打印出内容。