前端如何处理跨域

92 阅读2分钟

一、基础篇

跨域问题产生

跨域(Cross-Origin)是指在浏览器的同源策略(Same-Origin Policy)下,当一个网页尝试加载来自不同域名(协议、域名、端口有一个不同)的资源时,会出现跨域问题。同源策略是浏览器的一项安全策略,用于防止恶意网站从其他站点获取敏感数据。跨域问题的产生主要有以下原因:

  1. 安全性: 同源策略是为了保护用户隐私和数据安全而制定的。如果没有同源策略,恶意网站可能会轻松获取到其他网站的用户数据。
  2. 隔离数据: 同源策略有助于隔离不同网站的数据,防止不同网站之间的数据混合在一起,导致数据污染和安全问题。
  3. 跨域请求风险: 如果浏览器允许跨域请求,那么网站可能会被攻击者滥用,发起恶意请求,例如 CSRF(跨站请求伪造)攻击。

下面是一些常见产生跨域问题的情况:

  • 协议不同: 如果网页使用 HTTP 协议加载资源,但资源使用 HTTPS 协议,就会产生跨域问题。
  • 域名不同: 如果网页在一个域名上加载资源,但资源在另一个域名上,就会产生跨域问题。
  • 端口不同: 如果网页使用不同的端口加载资源,也会产生跨域问题。
  • 子域名不同: 同源策略也将子域名视为不同的域,例如,example.comsub.example.com 被视为不同的域。
  • JavaScript 的限制: 使用 XMLHttpRequest 或 Fetch API 发起跨域请求时,浏览器会检查请求的域是否符合同源策略。

解决跨域问题办法

1. JSONP(仅限GET请求):

<script>
  function handleResponse(data) {
    console.log('Response:', data);
  }
</script>
<script src="http://example.com/api/data?callback=handleResponse"></script>

在这个示例中,我们定义了一个名为 handleResponse 的JavaScript函数,然后通过<script>标签请求目标服务器的数据,该数据以回调函数的形式返回。请确保在目标服务器上支持JSONP请求。

vue项目里使用:

// 使用Vue Resource库
import Vue from 'vue';
import VueResource from 'vue-resource';

Vue.use(VueResource);

Vue.http.jsonp('http://example.com/api/data').then(response => {
  // 处理JSONP响应数据
});

2. CORS(跨源资源共享):

前端无需额外的代码来处理CORS,因为CORS配置是在服务器端完成的。确保目标服务器正确配置CORS标头以允许你的前端域名进行跨域请求。以下是一个示例CORS配置:

// Express.js服务器端示例
const express = require('express');
const cors = require('cors');
const app = express();

// 允许特定源的请求
app.use(cors({
  origin: 'http://your-frontend-app.com',
  methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
  credentials: true, // 允许跨域请求携带凭证(例如,使用Cookie)
}));

// 处理跨域请求
app.get('/api/data', (req, res) => {
  res.json({ message: 'Hello from the server!' });
});

const port = 3000;
app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});

前端无需额外代码,只需在前端应用中发送请求到服务器。

3. WebSocket连接:

WebSocket示例需要使用WebSocket客户端库,例如socket.io。以下是一个简化的WebSocket示例:

<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
<script>
  const socket = io('http://example.com'); // 连接到WebSocket服务器

  socket.on('connect', () => {
    console.log('Connected to WebSocket');
  });

  socket.on('message', (data) => {
    console.log('Received message:', data);
  });

  // 发送消息
  socket.emit('message', 'Hello, server!');
</script>

在这个示例中,我们使用了socket.io库来建立WebSocket连接,并通过WebSocket发送和接收消息。

4. 跨文档消息传递(PostMessage):

window.postMessage()方法允许你在不同窗口或iframe之间安全地传递消息。以下是一个简化的示例:

<!-- 在iframe的父窗口中 -->
<iframe src="http://example.com/iframe"></iframe>

<script>
  const iframe = document.querySelector('iframe');

  // 向iframe发送消息
  iframe.contentWindow.postMessage('Hello from parent!', 'http://example.com');
</script>

<!-- 在iframe中 -->
<script>
  window.addEventListener('message', (event) => {
    if (event.origin === 'http://example.com') {
      console.log('Received message from parent:', event.data);
    }
  });
</script>

在这个示例中,父窗口通过postMessage()向iframe发送消息,iframe监听message事件来接收消息。

请根据你的具体需求选择适当的方法,并确保在服务器端进行适当的配置(例如CORS配置)以支持跨域请求。前端示例只是演示如何处理跨域的基本方法。

二、在vue项目里处理跨域(反向代理)

  1. 在Vue项目的根目录下创建一个vue.config.js文件(如果已经存在,请忽略此步骤)。
  2. vue.config.js文件中添加以下内容:
module.exports = {
  devServer: {
    proxy: {
      // 代理配置
      '/api': {
        target: process.env.VUE_APP_API_BASE_URL, // 使用环境变量作为目标服务器URL
        changeOrigin: true,
        pathRewrite: {
          '^/api': '',
        },
      },
    },
  },
};

在上面的示例中,我们使用了process.env.VUE_APP_API_BASE_URL作为目标服务器的URL,这个环境变量是从Vue项目的.env文件中读取的。 3.在Vue项目的根目录下创建一个.env文件,以定义环境变量:

VUE_APP_API_BASE_URL=http://example.com/api

在这个示例中,我们将VUE_APP_API_BASE_URL设置为目标服务器的URL。

4.axios二次封装 当封装Axios时,你可以添加请求拦截器和响应拦截器来全局处理请求和响应。这使你能够在每个请求之前和每个响应之后执行一些操作,例如设置请求头、处理错误等。以下是如何在封装Axios时添加请求拦截和响应拦截的示例:

// api.js

import axios from 'axios';

const instance = axios.create({
  baseURL: process.env.VUE_APP_API_BASE_URL,
  timeout: 10000,
  headers: {
    'Content-Type': 'application/json',
  },
});

// 请求拦截器
instance.interceptors.request.use(
  config => {
    // 在请求发送之前做一些操作,例如添加请求头
    config.headers.Authorization = 'Bearer ' + getToken(); // 示例:添加身份验证令牌
    return config;
  },
  error => {
    // 处理请求错误
    return Promise.reject(error);
  }
);

// 响应拦截器
instance.interceptors.response.use(
  response => {
    // 在响应数据之前做一些操作
    return response;
  },
  error => {
    // 处理响应错误
    if (error.response) {
      // 如果服务器返回错误状态码,可以在这里处理
      console.error('HTTP Error:', error.response.status);
    } else if (error.request) {
      // 请求发出但没有收到响应
      console.error('Request Error:', error.request);
    } else {
      // 其他错误
      console.error('Error:', error.message);
    }
    return Promise.reject(error);
  }
);

export default instance;

在上面的示例中,我们通过interceptors属性添加了请求拦截器和响应拦截器。

  • 请求拦截器(interceptors.request)用于在每个请求发送之前执行操作。这里我们示例性地添加了身份验证令牌到请求头中。
  • 响应拦截器(interceptors.response)用于在每个响应到达之后执行操作。在这里,你可以处理响应数据或错误。

在Vue项目中,你只需导入这个封装好的Axios实例(例如,在userService.js中导入),然后使用它来发送请求,拦截器会自动应用到每个请求和响应中。

// userService.js

import axios from './api';

export default {
  getUserById(id) {
    return axios.get(`/users/${id}`);
  },
  updateUser(id, data) {
    return axios.put(`/users/${id}`, data);
  },
  // 可以添加其他API请求方法
};