跨域
浏览器同源策略
源地址的 "协议 + 域名 + 端⼝" 三者都相同,即使两个不同域名指向了同⼀ 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 请求数据
前端代码
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);
});
后端代码
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 解决跨域
前端代码
const res = await fetch('http://localhost:3001/api/json');
const data = await res.json();
console.log(data);
后端代码
// 创建一个 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);
代理服务器
说明: 同源策略, 是浏览器的安全策略, 服务器于服务器之间, 没有跨域问题,所以可以利用代理服务器转发请求。
简单实现代理服务器解决跨域
请求代码
const res = await fetch('/api/json');
const data = await res.json();
console.log(data);
vite.config.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 中处理跨域),代理服务器将请求转发到目标服务器上,然后目标服务器返回数据给客户端。
简单步骤
启动后端服务:
- 在 Windows 本地运行后端服务,监听 3001 端口(确保服务能处理 /json 等接口)。
配置 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
前端发起请求
前端请求地址
开发环境:
http://localhost/api/json
(Nginx 在本地)生产环境:
http://你的域名/api/json
或http://公网IP/api/json
浏览器访问 http://localhost/api/json(实际工作中访问的就是对应 Nginx 服务器的地址),流程如下:
- Nginx 拦截请求:匹配 /api/ 路径
- 转发到后端:实际请求 http://宿主机 IP:3001/json
- 返回数据:Nginx 添加跨域头后返回给浏览器