在使用 http-proxy-middleware 代理到 websocket 服务器之前,如何进行一些验证?

Posted

技术标签:

【中文标题】在使用 http-proxy-middleware 代理到 websocket 服务器之前,如何进行一些验证?【英文标题】:How can I involve some validation before proxying to a websocket server with http-proxy-middleware? 【发布时间】:2021-09-01 22:37:34 【问题描述】:

我可能搞错了。

目标:通过 apache 接收 https 连接,并在将其路由到 websocket 服务器之前进行一些验证。如果无效则返回 403 状态码,如果有效则继续进行 websocket 连接。

我决定尝试为此使用基于 http-proxy-middleware 的节点代理。在 apache 中,我有一个虚拟主机条目,其中包括:

  <Location /testws>
    ProxyPass        ws://127.0.0.1:6006/testws
    ProxyPassReverse        ws://127.0.0.1:6006/testws
  </Location>

我在 6006 端口上运行的节点代理是:

const express = require('express');
const  createProxyMiddleware, responseInterceptor  = require('http-proxy-middleware');

const app = express();

// proxy middleware options
const wsProxyOptions = 
    target: 'http://example.org', // target host

    changeOrigin: true, // needed for virtual hosted sites
    ws: true, // proxy websockets
    router: 
      // when request.headers.host == 'dev.localhost:3000',
      // override target 'http://www.example.org' to 'http://localhost:8000'
      // 'dev.localhost:3000': 'http://localhost:8000',
      '/testws': 'ws://127.0.0.1:6082',  //my websocket server is running on port 6082
    ,
    selfHandleResponse: true,
    onProxyRes: responseInterceptor(async (responseBuffer, proxyRes, req, res) => 
        res.statusCode = 418; // set different response status code
        const response = responseBuffer.toString('utf8');
        return response.replace('Example ', 'NewText');
    ),
    logLevel: 'debug'
;

// The setup below is recommended for external websocket upgrades
// https://github.com/chimurai/http-proxy-middleware#external-websocket-upgrade
const wsProxy = createProxyMiddleware(wsProxyOptions);
app.use(wsProxy);

const server = app.listen(6006);
server.on('upgrade', wsProxy.upgrade); // optional: upgrade externally

当我使用该设置时,我会从浏览器转到https://server.ip/testws。我按预期路由到我的 websocket 服务器。但是, onProxyRes(responseInterceptor) 永远不会被访问。我还尝试了其他事件选项,例如 onProxyReq 和 onProxyReqWs。他们也不会被击中。

接下来,我尝试将 apache 设置中的 ProxyPass 和 ProxyPassReverse 设置更改为 http://127.0.0.1:6006/testws 而不是 ws://127.0.0.1:6006/testws。现在,onProxyReq 确实被访问了,但是我从我的 websocket 服务器得到了一个响应,说“需要升级”,这可能是有道理的,因为升级还没有发生。可以在这里升级连接吗?如果有,怎么做?

我一直在尝试的另一件事是涉及路由器选项的异步功能。这对我来说可以确定新目标应该是什么,但似乎没有办法从那里涉及状态代码,因为它只想要返回一个目标值。

我想尝试 responseInterceptor 的原因是因为我想在连接到我的 websocket 服务器之前进行一些验证,如果验证失败,则返回状态代码 403,而不是继续使用 websocket 连接。对此有什么想法吗?或者,对于我正在尝试做的事情,这是错误的方法吗?

更新:我想我不需要 onProxyRes 是有道理的,因为升级可能在此之前发生。我仍然不确定为什么 onProxyReq 选项不起作用。不管怎样,我开始看:

server.on('upgrade', wsProxy.upgrade);

如果验证失败,我能否以某种方式回复状态码?例如:

server.on('upgrade', (req, socket, head) => 
    if (valid) 
        wsProxy.upgrade(req, socket, head);
     else 
      //somehow return status 403
    
);

【问题讨论】:

【参考方案1】:

看起来这篇文章问的是同样的事情,但更清楚:

Intercept (and potentially deny) web socket upgrade request

我不确定是否有更合适的解决方案,但到目前为止提到的解决方案似乎对我有用。它基本上展示了如何在升级时使用 http 状态代码进行响应:

server.on('upgrade', (req, socket, head) => 
    if (validationPasses) 
        wsProxy.upgrade(req, socket, head);
     else 
        socket.write('HTTP/1.1 403 Access Denied\r\n' +
            'Upgrade: WebSocket\r\n' +
            'Connection: Upgrade\r\n' +
            '\r\n');

        socket.destroy();
        return;
    
);

【讨论】:

以上是关于在使用 http-proxy-middleware 代理到 websocket 服务器之前,如何进行一些验证?的主要内容,如果未能解决你的问题,请参考以下文章

在使用 http-proxy-middleware 代理到 websocket 服务器之前,如何进行一些验证?

使用axios以及http-proxy-middleware代理处理跨域的问题

React18使用 http-proxy-middleware代理跨域

插件http-proxy-middleware在vue-cli中的使用

React axios 使用 http-proxy-middleware 解决跨域问题小记

node.js中使用http-proxy-middleware请求转发给其它服务器