Skip to content

跨域问题

跨域几种解决方式

什么是跨域

前端跨域问题是一个很常见的网络安全策略问题,主要是由于浏览器的同源策略(Same-originpolicy)导致的。同源策略限制了一个源(域名、协议、端口)的文档或脚本与另一个源的资源进行交互。这是为了防止恶意文档窃取数据或进行其他危害行为。
MDN 权威跨域解释

产生跨域的原因

  • 不同域名:例如,从 example.com 向 another.com 发送请求。
  • 不同协议:例如,从 http 的 example.com 向 https 的 example.com 发送请求。
  • 不同端口:例如,从 example.com:8080 向 example.com:3000 发送请求。

常见解决方案

CORS 解决

全称:Cross-Origin Resource Sharing
在服务端设置 Access-Control-Allow-Origin 等响应头,来声明允许哪些域进行资源共享(6 个头配置)。

javascript
/*
  ● Access-Control-Allow-Origin:指定哪些源可以访问资源。
  可以是具体的一个URL(如http://example.com),或者*表示允许任何源。
  ● Access-Control-Allow-Methods:指定允许的HTTP方法,
  如GET, POST, DELETE等。
  ● Access-Control-Allow-Headers:指定允许的请求头列表,
  浏览器会在预检请求中使用此头部告知服务器实际请求中会使用哪些头部。
  ● Access-Control-Allow-Credentials:指示是否允许发送Cookie。
  如果这个值是true,Access-Control-Allow-Origin就不能设置为*,
  必须指定明确的、与请求网页一致的域名。
  ● Access-Control-Max-Age:指定预检请求的结果能够被缓存多长时间。
  ● Access-Control-Expose-Headers:指定允许浏览器访问的服务器响应头。
  默认情况下,只有6个基本响应头是可以读的:Cache-Control、
  Content-Language、Content-Type、Expires、Last-Modified、Pragma。
*/
response.header("Access-Control-Allow-Origin", "*");
response.header("Access-Control-Allow-Methods", "*");
response.header("Access-Control-Allow-Headers", "*");

也可以借助 cors 中间件进行快速配置:
安装 cors 中间件:

shell
npm i cors

使用 cors 中间件:

javascript
const cors = require("cors");
// 使用默认配置允许所有源
app.use(cors());

也可以对 cors 进行具体配置

javascript
// 详细配置
app.use(
  cors({
    origin: "http://example.com", // 具体域名或使用函数动态确定
    methods: ["GET", "POST", "PUT", "DELETE"],
    allowedHeaders: ["Content-Type", "Authorization"],
    credentials: true,
    maxAge: 3600,
    exposedHeaders: ["Content-Length", "X-Karma-Count"],
  })
);

JSONP

全称:JSON with Padding
JSONP 是利用了 script 标签的可跨域能力

javascript
// 对应的回调函数(后期由服务端返回调用代码)
function handleResponse(data) {
  console.log(data);
  const tmp = document.getElementById("tmp");
  document.body.removeChild(tmp);
}

function getData() {
  // 创建一个script标签
  let script = document.createElement("script");
  // 设置好script标签的src属性(注意服务器需要支持JSONP)
  script.src = "http://localhost:8099/test-get?callback=handleResponse";
  // 给script标签追加id
  script.id = "tmp";
  // 将script标签添加到文档中
  document.body.appendChild(script);
}

服务端对应写法:

javascript
app.get("/test-get", (request, response) => {
  // 获取前端对应回到函数的名称
  const { callback } = request.query;
  // 要返回的数据
  const person = { name: "tony", age: 18 };
  // 拼接响应为函数调用字符串
  response.send(`${callback}(${JSON.stringify(person)})`);
});

Nginx 配置解决

打开 Nginx 配置文件进行编辑:

bash
sudo vim /etc/nginx/nginx.conf

在 http 模块下添加以下内容:

javascript
http {
  # 其他配置...

  # 添加跨域配置
  server {
    listen 80;
    server_name example.com;

    location / {
      add_header 'Access-Control-Allow-Origin' '*';
      add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
      add_header 'Access-Control-Allow-Headers'
        'DNT,User-Agent,X-Requested-With,If-Modified-Since,
          Cache-Control,Content-Type,Range';
      add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
    }
  }
}

保存并退出配置文件。
重启 Nginx

bash
sudo service nginx restart

前端配置代理

javascript
import { fileURLToPath, URL } from 'node:url'

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
  ],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    }
  }
  server: {
      //用来配置跨域
      host: '127.0.0.1',
      port: 8000,
      proxy: {
        '/api': {
          target: 'http://127.0.0.1:3000',//目标服务器地址
          changeOrigin: true,
          rewrite: (path) => path.replace(/^\/api/, '')
        },
      }
    }
})