Skip to content

跨域

浏览器同源策略

源地址的 "协议 + 域名 + 端⼝" 三者都相同,即使两个不同域名指向了同⼀ IP 地址,也被判断为⾮同源,就产生了跨域问题。

下面是一些地址的同源判断示例:

以下不同地址的页面, 去请求一个接口: http://store.company.com/getInfo

跨域

同源策略是 浏览器 的一种⽤于隔离潜在恶意⽂件的重要安全保护机制 (服务器没有这个策略限制)

在浏览器中,⼤部分内容都受同源策略限制,除了以下三个资源获取类型的标签:

  • <img>
  • <link>
  • script

如何实现跨域获取数据

主流方法有四种:

  • jsonp
  • 后端设置请求头(CORS)
  • 前端开发环境代理(webpack,vite 等)
  • nginx 反向代理

jsonp

这是一种非常经典的跨域方案,它利用了<script> 标签不受同源策略的限制的特性,实现跨域效果。

优点:

  • 实现简单
  • 兼容性好

缺点:

  • 只支持 GET 请求 (因为 <script> 标签只能发送 GET 请求)
  • 存在被 XSS 攻击的可能,缺乏安全性保证
  • 需要服务端配合改造

axios 中不支持 JSONP, 如果在开发中, 需要发送 JSONP 请求, 可以用 jsonp 插件

参考文档: Vue 中 JSONP 插件的使用

简单实现 jsonp 请求数据

前端代码

js
const jsonp = name => {
  let script = document.createElement('script');
  script.src = 'http://localhost:3000/api/jsonp?callback=' + name;
  document.body.appendChild(script);
  return new Promise((resolve, reject) => {
    window[name] = function (data) {
      resolve(data);
    };
  });
};

jsonp('callback' + Date.now()).then(data => {
  console.log(data);
});

后端代码

ts
const express = require('express');
const app = express();

app.get('/api/jsonp', (req, res) => {
  const { callback } = req.query;
  res.send(`${callback}('hello world')`);
});

app.listen(3000, () => {
  console.log('server is running at http://localhost:3000');
});

浏览器发起请求,返回数据的时候返回该函数名,并且将要返回的数据作为参数传入该函数中,浏览器会自动执行返回的 js 文件内容,从而达到传递参数的目的。

CORS (主流)

跨域资源共享(CORS),这是⽬前比较主流的跨域解决⽅案,

它利用一些额外的 HTTP 响应头来通知浏览器, 允许访问来自指定 origin 的非同源服务器上的资源。

Node.js 的 Express 框架的设置代码 (Java, PHP 等, 配置代码差不多):

简单实现 CORS 解决跨域

前端代码

js
const res = await fetch('http://localhost:3001/api/json');
const data = await res.json();
console.log(data);

后端代码

ts
// 创建一个 CORS 中间件
function allowCrossDomain(req, res, next) {
  res.header('Access-Control-Allow-Origin', 'http://localhost:3001');
  res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
  res.header('Access-Control-Allow-Headers', 'Content-Type');
  next();
}

// 为 Express 配置 CORS 中间件
app.use(allowCrossDomain);

代理服务器

说明: 同源策略, 是浏览器的安全策略, 服务器于服务器之间, 没有跨域问题,所以可以利用代理服务器转发请求。

简单实现代理服务器解决跨域

请求代码

js
const res = await fetch('/api/json');
const data = await res.json();
console.log(data);

vite.config.ts

ts
import { defineConfig } from 'vite';
export default defineConfig({
  server: {
    proxy: {
      //将以 /api 开头的请求代理到 http://localhost:3001 服务器
      '/api': {
        target: 'http://localhost:3001',
        //是否跨域
        changeOrigin: true,
        //路径重写 将 /api 替换成 '',主要看后端接口是否需要 /api 不需要的话就得重写
        // rewrite:path=>path.replace(/^\/api/,'')
      },
    },
  },
});

仅适用于开发环境解决跨域问题,webpack 等配置同理

nginx 代理

原理很简单,就是请求配有 nginx 的代理服务器(nginx 中处理跨域),代理服务器将请求转发到目标服务器上,然后目标服务器返回数据给客户端。

简单步骤

  1. ​ 启动后端服务:

    • 在 Windows 本地运行后端服务,监听 3001 端口(确保服务能处理 /json 等接口)。
  2. ​ 配置 WSL 中的 Nginx

    • 修改 Nginx 配置文件,添加以下规则:
    nginx
     # 精确匹配 如果是 /api/ 开头的路径(会匹配 /api/json等)
    	# 并且末尾斜杠会截断 /api 前缀 在实际请求时不会携带/api
     # 如果是 location /api (不带斜杠),会匹配 /api、/api123、/apixxx 等
    	# 并且不会截断 /api 前缀 实际请求时会携带 /api,/api123、/apixxx 等
    
    	location /api/ {
     proxy_pass http://宿主机IP:3001/;
     # 添加跨域响应头(以下三行)
     add_header 'Access-Control-Allow-Origin' '*';
     add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
     add_header 'Access-Control-Allow-Headers' 'Content-Type';
    
    # 处理 OPTIONS 预检请求
     if ($request_method = 'OPTIONS') {
         add_header 'Access-Control-Allow-Origin' '*';
         add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
         add_header 'Access-Control-Allow-Headers' 'Content-Type';
         add_header 'Content-Length' 0;
         return 204;
     }
    }
    • 重启 Nginx:sudo nginx -s reload
  3. 前端发起请求

    前端请求地址

  • 开发环境http://localhost/api/json (Nginx 在本地)

  • 生产环境http://你的域名/api/jsonhttp://公网IP/api/json

    浏览器访问 http://localhost/api/json(实际工作中访问的就是对应 Nginx 服务器的地址),流程如下:

    • ​Nginx 拦截请求:匹配 /api/ 路径
    • ​ 转发到后端:实际请求 http://宿主机 IP:3001/json
    • ​ 返回数据:Nginx 添加跨域头后返回给浏览器

Keep Reading, Keep Writing, Keep Coding