本文深入解析前端开发中常见的跨域问题及其解决方案。首先阐明浏览器同源策略对DOM、Cookie及Ajax的限制,随后详细对比CORS(含简单与复杂请求处理)、JSONP(基于Script标签的回调机制)以及代理服务器(Node.js中间件、Nginx及Vite配置)三种主流跨域解决方式,帮助开发者根据实际场景选择最优策略。
一、概念
1. 浏览器的同源策略
这是浏览器为了保障资源安全而强制执行的一种安全机制。
2. 源
源由协议、域名和端口三部分共同组成。
3. 同源和非同源
只有当两个源的协议、域名以及端口完全一致时,才被视为同源;否则即为非同源。
4. 同源请求和非同源请求
若请求源自同源环境,则称为同源请求;反之,若源自非同源环境,则称为非同源请求。
5. 跨域
当请求所处的源与目标源不一致时,就会引发跨域现象。
二、浏览器会对跨域做哪些限制
当源A与源B属于非同源关系时,浏览器会实施以下限制措施。
1. DOM 访问限制
源A中的脚本代码无法读取或操作源B的DOM元素。
2. Cookie 访问限制
源A无法读取或访问源B存储的Cookie信息。
3. Ajax 响应数据限制
源A可以向源B发起Ajax请求,但无法获取源B返回的具体数据内容。
三、注意点
跨域限制仅作用于浏览器端,服务端本身并不存在跨域限制的概念。
即使发生跨域,Ajax请求依然能够正常发出,只是响应数据不会交付给开发者处理。
通过<link>、<script>、<img>等标签发出的请求也可能涉及跨域,但浏览器对这些标签的限制较为宽松,对开发工作几乎无影响。
四、CORS 解决跨域
CORS即跨源资源共享(Cross-Origin Resource Sharing),这是一种允许Web应用服务器进行跨域访问控制的机制,使得不同源的网站之间能够安全地共享资源。
默认情况下,出于安全考虑,浏览器会限制从一个源(包含协议、域名、端口)加载的脚本去获取或操作另一个源的资源。CORS为Web应用提供了一种安全的方式来绕过这种同源策略的限制。
使用CORS解决跨域是最正统的方式,且要求服务器端由开发者自己控制。
1. 简单请求和复杂请求
简单请求
请求方式限定为GET、HEAD或POST。
Content-type限定为text/plain、multipart/form-data或application/x-www-form-urlencoded。
请求头字段必须符合《CORS安全规范》,一旦修改了请求头,该请求即变为复杂请求。
复杂请求
凡是不属于简单请求的请求均视为复杂请求,复杂请求会自动发送预检请求。
2. 解决简单请求
以Node.js为例,服务器端需设置响应头Access-Control-Allow-Origin,其值应设置为发送请求的源表示,只要是从该源发出的请求,均予以通过。若值为"*",则表示允许所有不同源发出的请求通过。
app.get("/xxx", (res, req) => {
res.setHeader("Access-Control-Allow-Origin", "发送请求源" | "*");
res.send();
});3. 解决复杂请求
以Node.js为例,服务器需设置响应头,使其与请求头一一对应,此时可使用cors插件进行配置。
app.use(
cors({
origin: "xxx", // 允许源
methods: ["GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS"], // 允许方法
allowedHeaders: ["xxx"], // 允许的自定义头
exposedHeaders: ["xxx"], // 要暴露的响应头
})
);
app.get("/xxx", (res, req) => {
res.setHeader("xxx", "xxx");
res.send();
});五、JSONP 解决跨域
JSONP利用了<script>标签可以跨域加载脚本,且不受严格模式限制的特性。
1. 基本流程
第一步:创建script标签,将src属性设置为跨域请求的URL。同时准备一个回调函数,用于处理返回的数据。
第二步:服务端接收到请求后,将数据封装在回调函数中返回。
第三步:客户端的回调函数被调用,数据以参数形式传入回调函数。
2. 例子
服务端代码
let list = [1, 2, 3];
app.get("/table", (req, res) => {
const { callback } = req.query;
res.send(`${callback}(${JSON.stringify(list)})`);
});客户端代码
function test(data) {
console.log(data);
}
function getTable() {
const script = document.createElement("script");
script.onload = () => {
script.remove();
};
script.src = "http://xxx.xx.xx:8080/table?callback=test";
document.body.appendChild(script);
}六、配置代理解决跨域
1. 自己配置代理服务器
借助http-proxy-middleware配置代理。
const { createProxyMiddleware } = require("http-proxy-middleware");
app.use(
"/api",
createProxyMiddleware({
target: "https://www.baidu.com",
changeOrigin: true,
pathRewrite: {
"^/api": "",
},
})
);2. 使用 Nginx 搭建代理服务器
2.1 设置响应头
server {
listen 80;
server_name your_domain.com;
location /api/ {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
if ($request_method = 'OPTIONS') {
return 204;
}
proxy_pass http://your_backend_server;
}
}2.2 反向代理
通过Nginx将跨域请求代理到目标服务器,使浏览器认为这是同源请求。
server {
listen 80;
server_name frontend.com;
location /api/ {
proxy_pass http://backend.com;
}
}3. 脚手架服务器
例如在Vue3中,配置文件vite.config.js可以配置代理的地址。
import { defineConfig } from "vite";
// https://vitejs.dev/config/
export default defineConfig(() => {
return {
server: {
host: "127.0.0.1",
port: 8081,
strictPort: false,
open: true,
proxy: {
"/dev-api": {
target: "http://localhost:1112",
changeOrigin: true,
rewrite: (path) => path.replace(/^\/dev-api/, ""),
},
},
},
};
});总结
到此这篇关于前端JS经典问题之跨域的解决方案的文章就介绍到这了,更多相关前端JS跨域解决方案内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!