文章目录
- 什么是跨域?
- 跨域的解决方案
- 部署在同一服务器
- CORS解决方案
- 代理服务器
- Node代理服务器
- Nginx反向代理
- 总结
什么是跨域?
要想理解跨域,要先理解浏览器的同源策略:
同源策略是浏览器中的一个重要的安全策略,它用于限制一个源(origin)的文档或者它加载的脚本如何能与另一个源的资源进行交互。它能帮助阻隔恶意文档,减少可能被攻击的媒介。
如果两个 URL的协议、主机、端口都相同的话,则这两个 URL 就是同源。
这个方案也被称为“
协议/主机/端口元组
”,或者直接是“元组”。
事实上跨域的产生和前端分离的发展有很大的关系:
在早期的服务器端渲染的阶段,是没有跨域的问题的;
但是随着前后端的分离,目前前端开发的代码和服务器开发的API接口往往是分离的,并且前端项目打包的静态资源和API接口甚至是部署在不同的服务器上的;在这种情况下我们就会发现,访问静态资源服务器和API接口服务器 很有可能不是同一个服务器或者不是同一个端口。浏览器发现静态资源和API接口(XHR、Fetch)请求不是来自同一个地方时(不满足同源策略),这个时候就产生了跨域。
所以,在静态资源服务器和API服务器(其他资源类同)是同一台服务器时,是没有跨域问题的。
跨域的解决方案
其实跨域的解决方案几乎都和服务器有关系,单独的前端
基本解决不了跨域
(虽然网上也能看到各种方案可以让单独的前端解决跨域,但是实际开发基本不会使用, 所以我说基本解决不了跨域), 因此想要彻底搞懂跨域的问题还需掌握一些服务器端的知识。有在webpack配置过跨域的同学可能就会有疑问, 我明明只需要在前端配置的webpack就可以解决跨域问题了, 那么为什么我还说单独的前端基本解决不了跨域呢?
其实在我们进行webpack配置是, 它的本质也是在webpack-server的服务器中配置了代理, 在我们进行了配置之后, webpack会开启一个node代理服务器, 并使用http-proxy-middleware
插件, 将请求转发到真实请求的服务器中, node开启代理服务器具体的做法我会在下面讲到。
那么在开发中跨域的问题我们要如何解决呢?
网上有很多跨域解决方案的汇总, 多到九种、十种甚至更多, 但是很多跨域的解决方案都是我们开发中很少(几乎都不会使用的方案), 比如: jsonp、postMessage、websocket等等… 这些不常用甚至不用的方案我就不再过多赘述, 下面给大家介绍三种开发中最为常见的跨域解决方案
部署在同一服务器
我们只需要遵守浏览器的同源策略, 将静态资源服和API服务器部署到同一台服务器下自然就不会有跨域的问题
CORS解决方案
跨源资源共享 (CORS, Cross-Origin Resource Sharing跨域资源共享):
CORS(跨域资源共享), 它是http的header机制, 该机制通过允许服务器标识除了它自己以外的其它源(域、协议和端口),使得浏览器允许这些被标识的源(origin)访问加载自己的资源。
浏览器又将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
如果只是简单请求, 我们只需要在服务器端进行以下配置即可(一般由后端开发进行配置):
"Access-Control-Allow-Origin", "*"
如果是非简单请求, 我们除上面之外, 还需要在服务器端进行以下配置:
"Access-Control-Allow-Origin", "*"
"Access-Control-Allow-Headers", "Accept, AcceptEncoding, Connection, Host, Origin"
"Access-Control-Allow-Credentials", true)
"Access-Control-Allow-Methods", "PUT, POST, GET, DELETE, PATCH, OPTIONS"
if (ctx.method === 'OPTIONS') {ctx.status = 204
} else {await next()
}
代理服务器
开发中常用代理服务器解决跨域方案有Node代理服务器和Nginx反向代理两种
Node代理服务器
Node代理服务器是平时开发中前端配置最多的一种方案, 像上面提到Webpack中进行代理配置, 就是采用的Node代理服务器。
实现思路:
我们将原本发送请求的服务器, 改变为Node代理服务器, 再由代理服务器向原本请求的服务器发送请求, 由于同源策略是在浏览器端的, 所以代理服务器发送请求是没有同源策略的限制。但是这样一来,浏览器和Node代理服务器会存在跨域的问题,这个时候,我们通常的做法是将Node代理服务器和我们的静态资源部署在同一服务器下就不会产生跨域的问题。当然,也可以通过CORS的方式给Node代理服务器配置跨域。
例如我们向代理服务器发送请求:
fetch('http://localhost:9000/api/users/list')
代理服务器通过http-proxy-middleware
插件, 在将请求转发到另一台服务器
const express = require('express')
const { createProxyMiddleware } = require('http-proxy-middleware')const app = express()app.use(express.static('./client'))app.use('/api', createProxyMiddleware({target: "http://localhost:8000",pathRewrite: {'^/api': ''}
}))app.listen(9000, () => {console.log('express proxy服务器开启成功')
})
Nginx反向代理
Nginx反向代理: 我们在向服务器发送请求时, 向Nginx服务器中进行发送, 再由Nginx服务器向原本的API服务器发送请求, 但是浏览器和Nginx服务器中存在跨域的问题, 我们需要在Nginx服务器中通过CORS进行跨域, 示例如下
location / {add_header Access-Control-Allow-Origin *;add_header Access-Control-Allow-Headers "Accept, AcceptEncoding, Connection, Host, Origin";add_header Access-Control-Allow-Credentials true;add_header Access-Control-Allow-Methods "PUT, POST, GET, DELETE, PATCH, OPTIONS";if ($method === 'OPTIONS') {return 204;}# 代理服务器请求的API服务器proxy_pass http://localhost:9000;
}
总结
那么在开发中上面跨域的解决方案我们该如何选择呢?
方案一: 如果是静态资源和API服务器部署在同一服务器下就不需要考虑跨域的问题, 但是实际开发中往往是分开部署的.
方案二: CORS是一种一劳永逸的解决方案, 我们只需要在服务器端进行一次配置, 就可以解决跨域的问题, 但是目前很多公司认为在这个方案在服务器端配置所有的源都可以进行访问, 是存在安全性问题的.
方案三: 代理服务器是目前使用最多的, 在开发阶段前端通过配置proxy开启一个Node代理服务器解决跨域; 在发布阶段, 我们在部署时会采用Nginx进行反向代理, 并且我们可以在Nginx中进行一些安全性的配置。