繁体   English   中英

Rails 应用程序中的代理 Websocket 请求

[英]Proxy Websocket Requests in Rails Application

我有一个 Rails 应用程序和一个 Golang 服务。 Rails 应用程序是带有 UI 的面向用户的应用程序。 来自前端的部分请求被路由到 golang 服务以实现某些功能。 到目前为止,我们只有来自 UI 的 HTTP 请求。

现在我们在 UI 中有 websockets 功能。 所以我们需要通过 Rails Application 将这些 websocket 请求路由到 Golang 服务。 基本上 Rails 应用程序控制身份验证/授权部分。 所以我们需要通过 Rails 应用程序路由请求。

我们探索了https://github.com/ncr/rack-proxy来路由请求,但我们无法正确路由 websocket 请求。 我们尝试了以下代码,其中 web 套接字请求在ws://localhost:3000/ws/v1/stat上接收,并使用以下代码发送到ws://localhost:4000/ws/v1/stats

# frozen_string_literal: true
require 'rack-proxy'
module Proxy
  ENV['SERVICE_URL'] ||= 'http://guides.rubyonrails.org'
  class GoServiceProxy < Rack::Proxy
    def perform_request(env)
      request = Rack::Request.new(env)
      # use rack proxy for anything hitting our host app at /example_service
      # if request.path =~ %r{^/example_service}
      if request.path =~ %r{stats}
        backend = URI(ENV['SERVICE_URL'])
        # most backends required host set properly, but rack-proxy doesn't set this for you automatically
        # even when a backend host is passed in via the options
        env["HTTP_HOST"] = 'localhost:4000'
        # This is the only path that needs to be set currently on Rails 5 & greater
        #env['PATH_INFO'] = ENV['SERVICE_PATH'] || '/configuring.html'
        # don't send your sites cookies to target service, unless it is a trusted internal service that can parse all your cookies
        env['HTTP_COOKIE'] = ''
        super(env)
      else
        @app.call(env)
      end
    end
  end
end

但是当我在ws://localhost:3000/ws/v1/stat上的 curl 上时,我得到 101 响应,但是从 go 服务发送的消息没有到来。 以下屏幕截图是我得到的回复:

curl -v --request GET 'http://localhost:3000/ws/v1/stats' \
--header 'Content-Type: application/json' \
--header 'Upgrade: websocket' \
--header 'Connection: upgrade' \
--header 'Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==' \
--header 'Sec-Websocket-Version: 13'
Note: Unnecessary use of -X or --request, GET is already inferred.
*   Trying ::1...
* TCP_NODELAY set
* Connection failed
* connect to ::1 port 3000 failed: Connection refused
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 3000 (#0)
> GET /ws/v1/stats HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.54.0
> Accept: */*
> Content-Type: application/json
> Upgrade: websocket
> Connection: upgrade
> Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
> Sec-Websocket-Version: 13
>
< HTTP/1.1 101 Switching Protocols
< sec-websocket-accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
< Cache-Control: no-cache
< X-Request-Id: 957758f9-d762-43d0-bffb-fdd72efbfbc1
< X-Runtime: 0.002848
<
* Empty reply from server
* Connection #0 to host localhost left intact
curl: (52) Empty reply from server

有什么方法可以通过 Rails 应用程序代理 websocket 请求?

这听起来像是一个 A/B 问题。

您真正的问题是使用 Rails 凭据在 Go 应用程序上验证 WebSocket 用户。

为什么不代理连接

您最新的解决方案似乎是让 Rails 对用户进行身份验证,然后将连接代理到 Go 应用程序。

该解决方案存在许多安全问题以及很大的性能损失。 例如:

  1. (安全)Go 应用程序假定所有连接都是“安全的”,通过让攻击者尝试直接或通过不同的路线(绕过 Rails 应用程序)连接到该服务,使其成为网络攻击的便捷载体。

  2. (性能)所有网络流量和连接都翻了一番(如果不超过一倍,实际上),给 Rails 应用程序带来了更高的负载。

更好的解决方案

更好的解决方案是编写一个小型 Rails AUTH 应用程序,为 Go 应用程序提供身份验证服务。

Go 应用程序执行身份验证。 假设任何网络流量都是安全的(即使是看似本地的流量)都是灾难的根源。

这些身份验证服务可能会测试通过 Rails 面向用户的应用程序预先协商的长期身份验证令牌(安全性差)或一次性身份验证令牌(更好的安全性)。

WebSocket 连接将直接连接到 Go 应用程序(很可能使用子域或 URL 路由)并使用身份验证微服务进行验证。

祝你好运!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM