利用代理解决跨域请求问题
前前后后也写了不少 node 项目了,但一直存在一个痛点: 浏览器跨域请求限制。我们知道,同源策略(Same Origin Policy, SOP)是浏览器最核心也是最基本的安全功能,所谓同源是指协议+域名+端口
三者必须完全一致。一旦前端请求地址与后台接口地址不同源,则被浏览器视为跨域请求,会立即被拦截。
我们在哪些情形下需要跨域呢?比如本地开发时,localhost:端口
与后台接口地址不同源,上线生产时,node 一般是直接监听 IP地址:端口
,而前端页面一般挂在域名下,因此也是不同源的。这两种情形都会受到浏览器的限制,严重影响开发体验。
对于以上问题,很多大佬整理了不少跨域方案,如 前端常见跨域解决方案(全),这位老哥就在这篇文章中针对不同情形总结了至少 9 种跨域方案。经过实践,发现很多方法可行性都太低,不是步骤繁杂,就是限制太多。
在实际操作中我的经验是基于代理的跨域请求方案相对稳健,它有以下几个优点:
- 不需要另外对外开放端口。因为 nginx 和 node 一般都是在同一个服务器上,所以 nginx 与 node 的通信都在本地进行,不需要把 node 监听的端口对外开放,增加了服务器的安全性。
- 顺便解决了 https 证书问题。node 和 nginx 的通信协议可以使用 http,而前端页面与后端接口的通信仍可以采用 https 协议。
- 本地开发和上线生产的接口一致,不需要进行额外的修改。
实战
在生产环境的可以通过修改的 nginx 的 vhost 配置文件实现代理,而在本地开发则需要视项目的大小使用 browser-sync
中间件、Webpack 代理或其它反向代理工具。
生产环境
首先写一个最简单的 app.js,这里以监听 50000
端口为例: 1
2
3
4
5
6const http = require('http')
const port = 50000
const server = http.createServer((req, res) => {
res.end('Hello World.')
}).listen(port)
接着修改 nginx 配置文件,使其能够对特定路径进行反向代理: 1
2
3
4
5location /api/
{
proxy_pass http://localhost:50000/;
proxy_set_header X-Real-IP $remote_addr;
}
注意
proxy_pass http://localhost:50000/;
和proxy_pass http://localhost:50000;
是不一样的,虽然两者只差一个/
,但意义却截然不同。当域名是https://www.lolimay.cn
、访问的 url 是https://www.lolimay.cn/api/interface
,那么使用proxy_pass http://localhost:50000/;
得到的req.url
是/interface
,而使用proxy_pass http://localhost:50000;
得到的req.url
是/api/interface
。
通过以上的配置我们就可以通过直接访问 /api
来访问 http://localhost:50000/
下的后端接口了。需要注意的是,由于中间经过了一层代理,所以直接通过 headers
拿到的 IP 地址 127.0.0.1
,而不是客户端的真实 IP
。我们在配置中另外增加了 proxy_set_header X-Real-IP $remote_addr;
这个规则,这样我们就可以通过 req.headers['x-real-ip']
来获得客户端的真实 IP。
开发环境
在开发环境中,对于比较简单的小项目,个人倾向于使用 browser-sync
作本地服务器环境。它支持热更新,并可以通过简单的配置实现 api
部分的 proxy
代理。
我们通过以下的配置来利用 browser-sync
的 middleware
实现 api
接口的反向代理:
- 初始化项目
browser-sync init
- 安装
http-proxy-middleware
模块 - 修改
bs-config.js
,在文件顶部添加以下内容:bs-config.js 1
2
3
4
5
6const proxy = require('http-proxy-middleware')
const middleware = proxy('/api',{
target: 'https://demo.com/api',
changeOrigin: true
})
小结
在对服务器有完全的控制权限的情况下,使用反向代理来突破浏览器的跨域请求限制是一种不错的解决方案。