第十六节:跨域的原因及常用跨域方案的总结(同源部署、CORS、node代理、Nginx反向代理等)
一. 跨域简介
PS:之前.Net系列中的跨域总结
https://www.cnblogs.com/yaopengfei/p/10340434.html (包含jsonp、script等古老的方式)
https://www.cnblogs.com/yaopengfei/p/11191938.html
1. 为什么会产生跨域?
浏览器存在一个“同源策略”,是一个重要的安全策略,它用于限制一个origin的文档或者它加载的脚本如何能与另一个源的资源进行交互。它能帮助阻隔恶意文档,减少可能被攻击的媒介。 (即两个不同源,从浏览器发出请求,是不能直接交互的)
如果两个URL的 协议、主机、端口,都相同,则就是同源的,就不存在的跨域的问题.
详见图1:
2. 什么是跨域?
两个URL的如果是不同源的,从浏览器中发出请求,则就会出现跨域问题,即无法请求成功。
如下图2:
Server代码:
const Koa = require("koa");
const app = new Koa();
app.listen(9090, () => {
console.log("---------------koa服务器启动成功,端口9090----------------");
});
//路由
const koaRouter = require("@koa/router");
const router = new koaRouter();
// Get请求
router.get("/getUserInfo", (ctx, next) => {
ctx.body = {
status: "ok",
msg: "请求成功",
data: [
{ id: 001, name: "ypf1", age: 18 },
{ id: 002, name: "ypf2", age: 18 },
{ id: 003, name: "ypf3", age: 25 },
],
};
});
客户端代码:
<body>
<h1>测试跨域——同源部署</h1>
<script>
// 发送-网络请求
fetch('http://127.0.0.1:9090/getUserInfo').then(async res => {
const result = await res.json()
console.log(result)
})
</script>
</body>
3. 实操准备
【npm install koa koa-static @koa/router】 -- koa相关
【npm install express】 -- express相关
【npm install http-proxy-middleware】 -- 代理中间件 (webpack中实际上使用就是这个中间件)
二. 同源部署
1. 说明
几年前,当还没有前后端分离的时候,前端页面和接口是融合在一起的,即 都部署在同一个地址/端口下,也就不存在跨域问题。
所以这里所谓的同源部署,就是将 前端页面 和 Api接口部署在 同协议、同主机、端口下。
2. 实操
Server端开启:app.use(static("../client"));
在浏览器中输入:http://127.0.0.1:9090/01-跨域_同源部署演示.html
结果:可以请求成功
server代码:
app.listen(9090, () => {
console.log("---------------koa服务器启动成功,端口9090----------------");
});
//路由
const koaRouter = require("@koa/router");
const router = new koaRouter();
// 将前端页面的也部署在这个服务器下
app.use(static("../client"));
// Get请求
router.get("/getUserInfo", (ctx, next) => {
ctx.body = {
status: "ok",
msg: "请求成功",
data: [
{ id: 001, name: "ypf1", age: 18 },
{ id: 002, name: "ypf2", age: 18 },
{ id: 003, name: "ypf3", age: 25 },
],
};
});
客户端代码:
<body>
<h1>测试跨域——同源部署</h1>
<script>
// 发送-网络请求
fetch('http://127.0.0.1:9090/getUserInfo').then(async res => {
const result = await res.json()
console.log(result)
})
</script>
</body>
三. CORS跨域资源共享
1. 含义
跨源资源共享(CORS,Cross-Origin Resource Sharing跨域资源共享):
(1).它是一种基于http header的机制;
(2).该机制通过允许服务器标示除了它自己以外的其它源(域、协议和端口),使得浏览器允许这些 origin 访问加载自己的资源。
2. CORS分类
浏览器将 CORS 请求分成两类:简单请求和非简单请求。
◼ 只要同时满足以下两大条件,就属于简单请求(不满足就属于非简单请求)(了解即可)。
◼ 请求方法是以下是三种方法之一: HEAD GET POST
◼ HTTP 的头信息不超出以下几种字段:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三个值 application/x-www-form-urlencoded、multipart/form-data、text/plain
3. 实操
虽然简单请求和非简单请求配置有差距,但后者是包含前者的,所以按照非简单请求配置即可。
Server端口:通过一个中间件配置一下header参数,核心参数为:Access-Control-Allow-Origin,详见:02-Server(cors).js
在浏览器中访问:open with live server的方式访问 5050端口,02-cors方案.html页面
结果:可以请求成功。
server代码:
app.listen(9090, () => {
console.log("---------------koa服务器启动成功,端口9090----------------");
});
//路由
const koaRouter = require("@koa/router");
const router = new koaRouter();
// CORS---针对简单请求
/* app.use(async (ctx, next) => {
// 1.允许开启CORS
// * 代表所有源, 也可以写某个具体源:比如:http://127.0.0.1:8090
ctx.set("Access-Control-Allow-Origin", "*");
await next();
}); */
// CORS---针对非简单请求
app.use(async (ctx, next) => {
//1.允许开启CORS--简单请求
ctx.set("Access-Control-Allow-Origin", "*");
//2.复杂请求还需要额外开启后面的代码
ctx.set(
"Access-Control-Allow-Headers",
"Accept, AcceptEncoding, Connection, Host, Origin"
);
ctx.set("Access-Control-Allow-Credentials", true); // cookie
ctx.set(
"Access-Control-Allow-Methods",
"PUT, POST, GET, DELETE, PATCH, OPTIONS"
);
// 3.发起的是一个options请求
if (ctx.method === "OPTIONS") {
ctx.status = 204; //表示 no content
} else {
await next();
}
});
// Get请求
router.get("/getUserInfo", (ctx, next) => {
ctx.body = {
status: "ok",
msg: "请求成功",
data: [
{ id: 001, name: "ypf1", age: 18 },
{ id: 002, name: "ypf2", age: 18 },
{ id: 003, name: "ypf3", age: 25 },
],
};
});
客户端代码:
<body>
<h1>测试Cors</h1>
<script>
// 发送-网络请求
fetch('http://127.0.0.1:9090/getUserInfo').then(async res => {
const result = await res.json()
console.log(result)
})
</script>
</body>
四. node代理服务器 【开发环境推荐】
1. 含义
首先,不能跨域是因为浏览器的同源策略导致的,如果是node服务器,就可以任意访问,随便跨域了,所以 在浏览器 和 后台api中间,加一个中转,这个中转就是node服务器, 但是node服务器和浏览器还是存在跨域问题,这里 对node服务器自身而言,还是要用 那两套方案:
1 部署同源部署
2 开启cors
这里通常采用同源部署。
2. 实操
(1). 安装代理中间件 【npm install http-proxy-middleware】
(2). 配置代理服务器,这里为了区分,使用express进行配置
A. 需要和前端页面配置同源策略,解决前端和代理服务器直接的跨域问题。 app.use(express.static("../client"));
B. 配置代理:
监听请求中含 /api/的,然后进行重写为空字符串"", 然后使用target中的地址进行拼接,target中的地址就是api服务器的地址
(3). 前端页面,请求的地址要改为 代理服务器的地址,且以api开头
如:http://127.0.0.1:8080/api/getUserInfo
(4).启动测试:
代理服务器: 8080 【proxy-server.js】
api服务器: 9090 【 03-Server(纯ApiServer).js 】
前端: http://127.0.0.1:8080/03-node代理方案.html 【这里必须用代理服务器的地址启动,否则前端和代理之间也存在跨域问题哦】
测试结果:最终前端页面的里的地址 http://127.0.0.1:8080/api/getUserInfo 会转换成: http://127.0.0.1:9090/getUserInfo,访问成功
api-server代码:
app.listen(9090, () => {
console.log("---------------koa服务器启动成功,端口9090----------------");
});
//路由
const koaRouter = require("@koa/router");
const router = new koaRouter();
// Get请求
router.get("/getUserInfo", (ctx, next) => {
ctx.body = {
status: "ok",
msg: "请求成功",
data: [
{ id: 001, name: "ypf1", age: 18 },
{ id: 002, name: "ypf2", age: 18 },
{ id: 003, name: "ypf3", age: 25 },
],
};
});
代理服务器代码:
const express = require("express");
const app = express();
const { createProxyMiddleware } = require("http-proxy-middleware");
// 代理服务器和前端页面进行同源部署
app.use(express.static("../client"));
// 代理配置
app.use(
"/api",
createProxyMiddleware({
target: "http://localhost:9090",
pathRewrite: {
"^/api": "", //表示请求地址中 xxxx/api 会被重写为空"", 然后转换成上述的target中的 http://localhost:9090 + 后面的地址
},
})
);
app.listen(8080, () => {
console.log("------------express服务器启动成功了, 端口为 8080------------");
});
客户端代码:
<body>
<h1>Node代理方案</h1>
<script>
// 发送-网络请求 (这里请求的是代理服务器)
fetch('http://127.0.0.1:8080/api/getUserInfo').then(async res => {
const result = await res.json()
console.log(result)
})
</script>
</body>
3. 扩展webpack代理配置
详见官网:https://webpack.docschina.org/configuration/dev-server/#devserverproxy
webpack也是基于 中间件 http-proxy-middleware 来封装的。
代码如下:
4. 扩展vite的代理配置
详见官网:https://cn.vitejs.dev/config/server-options.html#server-proxy
代码如下:
五. nginx反向代理 【生产环境推荐】
1. 含义
这里的nginx相当于一个中转的代理服务器(nginx和api-server之间不存在跨域哦),用于转发 前端的请求 给 api-server,但是 前端和nginx之间, 同样存在跨域问题,这里需要给nginx配置cors,最终就实现 前端→ proxy-server → api-server
2. 实操
(1). nginx配置
监听7070端口,反向代理到 http://127.0.0.1:9090 , 同时配置cors,便于前端可以访问nginx
(2). 启动
Nginx服务器:7070
api服务器: 9090 【 03-Server(纯ApiServer).js 】
前端: http://127.0.0.1:5050/11-跨域方案总结/client/04-nginx反向代理方案.html 【open with live server的方式 5050端口】
测试结果: 最终前端页面的里的地址 http://127.0.0.1:7070/api/getUserInfo 会转换成: http://127.0.0.1:9090/getUserInfo
app.listen(9090, () => {
console.log("---------------koa服务器启动成功,端口9090----------------");
});
//路由
const koaRouter = require("@koa/router");
const router = new koaRouter();
// Get请求
router.get("/getUserInfo", (ctx, next) => {
ctx.body = {
status: "ok",
msg: "请求成功",
data: [
{ id: 001, name: "ypf1", age: 18 },
{ id: 002, name: "ypf2", age: 18 },
{ id: 003, name: "ypf3", age: 25 },
],
};
});
server {
listen 7070; # 监听的端口
server_name localhost; # 监听的地址
# 通用匹配
location / {
#配置cors
add_header 'Access-Control-Allow-Origin' *;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, OPTIONS';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Headers' 'Accept,Accept-Encoding,Accept-Language,Connection,Content-Length,Content-Type,Host,Origin,Referer,User-Agent';
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
# option类请求是跨域的先验请求
if ($request_method = 'OPTIONS') {
return 204; # 204代表正常
}
#代理地址
proxy_pass http://127.0.0.1:9090;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
<body>
<h1>Nginx反向代理方案</h1>
<script>
// 发送-网络请求 (这里请求的是代理服务器)
fetch('http://127.0.0.1:7070/getUserInfo').then(async res => {
const result = await res.json()
console.log(result)
})
</script>
</body>
六. 其它方案
jsonp
script标签
websocet、SignalR
postMessage
chrome的配置
详见开篇之前的文档
!
- 作 者 : Yaopengfei(姚鹏飞)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 声 明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
- 声 明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。