前端开发中的跨域问题详解

160 阅读2分钟

什么是跨域?

跨域(Cross-Origin)是指浏览器的同源策略(Same-Origin Policy)阻止了不同源之间的资源访问。"同源"要求以下三个部分都相同:

  • 协议(Protocol):如 http/https

  • 域名(Domain):如 localhost、example.com

  • 端口(Port):如 8080、3000

例如:

前端地址:http://localhost:8080
后端地址:http://127.0.0.1:8000

这种情况就属于跨域(端口不同)。

为什么会有跨域限制?

跨域限制是浏览器的一种安全机制,目的是防止恶意网站获取其他网站的敏感数据。这种限制只存在于浏览器端,服务器之间的通信是不受此限制的

开发环境中的跨域

1. 代理方式(前端配置)

通过在开发服务器配置代理来解决:

devServer: {
  proxy: {
    '/api': {
      target: 'http://127.0.0.1:8000',
      changeOrigin: true
    }
  }
}

工作原理:

浏览器 ---> 开发服务器(8080) ---> 后端服务器(8000)

优点:

  • 不需要修改后端代码

  • 只在开发环境中生效

  • 配置简单

2. CORS(后端配置)

# Python/Django示例
response.headers['Access-Control-Allow-Origin'] = '*'
response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE'
response.headers['Access-Control-Allow-Headers'] = 'Content-Type'

3. JSONP(已过时)

只支持GET请求,现代开发中较少使用。

生产环境的解决方案

1.同源部署:
  • 将前端资源部署到后端服务器

  • 前后端使用相同的域名

2.配置CORS:
  • 后端正确配置CORS响应头

  • 允许特定域名访问

3.nginx反向代理:
location /api {
    proxy_pass http://backend-server;
}

实际项目配置示例

1.开发环境配置(.env.development):

VUE_APP_API_URL=/api
VUE_APP_BASE_URL=http://127.0.0.1:8000

2.生产环境配置(.env.production):

VUE_APP_API_URL=/api
VUE_APP_BASE_URL=http://production-api.com

3.代理配置(vue.config.js):

devServer: {
  proxy: {
    '/api': {
      target: process.env.VUE_APP_BASE_URL,
      changeOrigin: true
    }
  }
}

常见问题

OPTIONS预检请求:
  • 复杂请求会先发送OPTIONS请求

  • 需要后端正确响应OPTIONS请求

Cookie跨域:
  • 需要设置 withCredentials: true

  • 后端设置 Access-Control-Allow-Credentials: true

  • Access-Control-Allow-Origin 不能为 *

代理失效:
  • 检查代理配置是否正确

  • 确认目标服务器是否可访问

  • 查看开发服务器控制台错误信息

总结

  • 跨域是浏览器的安全限制,不是后端的限制

  • 开发环境推荐使用代理方式解决跨域

  • 生产环境建议使用同源部署或正确配置CORS

  • 选择合适的跨域解决方案要考虑

我自己项目上的实际配置

.env.development文件

VUE_APP_API_URL=/api
# 指向本地服务器地址
VUE_APP_BASE_URL=http://127.0.0.1:8000
# 指向远程服务器地址
# VUE_APP_BASE_URL=http://xxx

.vue.config.js文件

const { defineConfig } = require('@vue/cli-service')
const webpack = require('webpack')

module.exports = defineConfig({
  transpileDependencies: true,
  lintOnSave: true,
  devServer: {
    proxy: {
      '/api': {
        target: process.env.VUE_APP_BASE_URL,
        changeOrigin: true,
        // pathRewrite: {  // 如果后端接口没有使用 /api 前缀,则需要注释掉
        //   '^/api': ''
        // }
      },
      '/storage': {
        target: process.env.VUE_APP_BASE_URL,
        changeOrigin: true
      }
    }
  },
  configureWebpack: {
    plugins: [
      new webpack.DefinePlugin({
        __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: JSON.stringify(false)
      })
    ]
  }
})