利用代理解决跨域请求问题

利用代理解决跨域请求问题

前前后后也写了不少 node 项目了,但一直存在一个痛点: 浏览器跨域请求限制。我们知道,同源策略(Same Origin Policy, SOP)是浏览器最核心也是最基本的安全功能,所谓同源是指协议+域名+端口三者必须完全一致。一旦前端请求地址与后台接口地址不同源,则被浏览器视为跨域请求,会立即被拦截。

我们在哪些情形下需要跨域呢?比如本地开发时,localhost:端口 与后台接口地址不同源,上线生产时,node 一般是直接监听 IP地址:端口,而前端页面一般挂在域名下,因此也是不同源的。这两种情形都会受到浏览器的限制,严重影响开发体验。

对于以上问题,很多大佬整理了不少跨域方案,如 前端常见跨域解决方案(全),这位老哥就在这篇文章中针对不同情形总结了至少 9 种跨域方案。经过实践,发现很多方法可行性都太低,不是步骤繁杂,就是限制太多。

在实际操作中我的经验是基于代理的跨域请求方案相对稳健,它有以下几个优点:

  1. 不需要另外对外开放端口。因为 nginx 和 node 一般都是在同一个服务器上,所以 nginx 与 node 的通信都在本地进行,不需要把 node 监听的端口对外开放,增加了服务器的安全性。
  2. 顺便解决了 https 证书问题。node 和 nginx 的通信协议可以使用 http,而前端页面与后端接口的通信仍可以采用 https 协议。
  3. 本地开发和上线生产的接口一致,不需要进行额外的修改。

实战

在生产环境的可以通过修改的 nginx 的 vhost 配置文件实现代理,而在本地开发则需要视项目的大小使用 browser-sync 中间件、Webpack 代理或其它反向代理工具。

生产环境

首先写一个最简单的 app.js,这里以监听 50000 端口为例:

app.js
1
2
3
4
5
6
const http = require('http')
const port = 50000

const server = http.createServer((req, res) => {
res.end('Hello World.')
}).listen(port)

接着修改 nginx 配置文件,使其能够对特定路径进行反向代理:

vhost.conf
1
2
3
4
5
location /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-syncmiddleware 实现 api 接口的反向代理:

  1. 初始化项目 browser-sync init
  2. 安装 http-proxy-middleware 模块
  3. 修改 bs-config.js,在文件顶部添加以下内容:
    bs-config.js
    1
    2
    3
    4
    5
    6
    const proxy = require('http-proxy-middleware')

    const middleware = proxy('/api',{
    target: 'https://demo.com/api',
    changeOrigin: true
    })

小结

在对服务器有完全的控制权限的情况下,使用反向代理来突破浏览器的跨域请求限制是一种不错的解决方案。

参考链接

  1. JavaScript跨域总结与解决办法
  2. 前端常见跨域解决方案(全)