1

在 websocket-rails 的wiki上,它有以下使用CanCan gem 的示例。

这怎么行?对于普通的 http 请求,发送的 cookie 具有标识用户的令牌,但是使用 websockets 发送原始数据并且没有发送 cookie 那么服务器如何使用 CanCan gem 识别用户是谁?

class AuthorizationController < WebsocketRails::BaseController
  def authorize_channels
    # The channel name will be passed inside the message Hash
    channel = Channel.find_by_name message[:channel]
    if can? :subscribe, channel
      accept_channel current_user
    else
      deny_channel {:message => 'authorization failed!'}
    end
  end
end

编辑:

在下面dispatcher创建的时候,应该发送一个包含Upgrade: websocketcookie 的 http 请求。但在下一行subscribe_private没有创建 websocket 连接,因此它不是 http 请求,并且可能无法自动访问 cookie。

// connect to server like normal
var dispatcher = new WebSocketRails('localhost:3000/websocket');

// subscribe to the channel
var private_channel = dispatcher.subscribe_private('channel_name');
4

1 回答 1

5

它不完全是原始数据,它是将 http 协议升级为保持连接打开的套接字,如果您查看wikipedia上的文档,在创建连接时的握手请求中,有一个信息流正在发送来自客户端,以及来自服务器的答案。

所以一个 websocket 连接请求的例子是

GET /mychat HTTP/1.1
Host: server.example.com 
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat
Sec-WebSocket-Version: 13
Origin: http://example.com

它作为字节流通过套接字发送,但是同样的事情发生在一个普通的 http 请求中,然而,

如果您检查使用 rails-websocket 创建的请求,当您在 js 上运行代码时

var dispatcher = new WebSocketRails('localhost:3000/websocket');

您将看到通过网络连接的请求是

GET /websocket HTTP/1.1
Host: localhost:3000
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:19.0) Gecko/20100101 Firefox/19.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,es-ar;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate
Sec-WebSocket-Version: 13
Origin: http://localhost:3000
Sec-WebSocket-Key: dRpM9EesBFdk3SOH2QL/Tw==
Cookie: __utma=111872281.1938357651.1354053248.1355759500.1357797379.3; __utmz=111872281.1354053248.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); hblid=T1PaqE0vcRC9zDYrpFoBo5RD91766581; olfsk=olfsk5917359536568161; remember_admin_user_token=BAhbB1sGaQlJIiIkMmEkMTAkV0VhZzJaeXg3SzZFQWMzVUdPLktaTwY6BkVU--f84238cbbcb767e075117603de67f56a7150eb97; _stack_session=BAh7CEkiD3Nlc3Npb25faWQGOgZFRkkiJTg0NGIwNzZmNWUyZjFiNTMwZDkwMWUyMGFiODMxOGE3BjsAVEkiEF9jc3JmX3Rva2VuBjsARkkiMWxIS1FHUjg1b2pDbjFybFY4RW8yemtzRWtVQUdHY1BxTGxtdzBWOFdBN009BjsARkkiE3VzZXJfcmV0dXJuX3RvBjsARiIZL2hvbWUvcHJpdmF0ZV9hY3Rpb24%3D--e4823c74756cf70af0675323fb752b1f87064f09
Connection: keep-alive, Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket

因此,cookie 正在发送,最重要的是,在您的 AuthorizationController 上:

class AuthorizationController < WebsocketRails::BaseController
  def authorize_channels
    # The channel name will be passed inside the message Hash
    channel = Channel.find_by_name message[:channel]
    if can?(:subscribe, channel)
      accept_channel current_user
    else
      deny_channel({:message => 'authorization failed!'})
    end
  end
end

如果你在 authorize_channels 方法上设置断点,你会看到你拥有所有的 cookie,就好像它是一个普通的 http 请求一样。

有关 websocket 的工作原理和方式的更多信息,您可以阅读RFC,但这里重要的是,在创建连接时的握手时,客户端会发送 cookie 以及其他信息,比如它是否是某种 http请求时,服务器接收 websocket 请求,通过检查 cookie 来验证用户身份,并且它要么打开保持打开的连接作为全双工通信的套接字,要么关闭连接,因为凭据无效。


我不确定我是否理解你的问题。普通页面请求和 Web 套接字之间的主要区别在于,Web 套接字是用户 http 握手以启动连接的套接字。

在您的代码中,这些行发送带有 cookie 的 http 请求,并且服务器接受连接。

// connect to server like normal
var dispatcher = new WebSocketRails('localhost:3000/websocket');

所以此时你有一个全双工套接字(这意味着服务器或客户端都可以通过它发送数据而不会发生冲突)。任意数据。

然后在下一个代码上:

// subscribe to the channel
var private_channel = dispatcher.subscribe_private('channel_name');

客户端正在请求服务器(通过活动的连接,通过 websocket)订阅私有通道。

现在,要了解的主要重要事项是,如果您订阅了一个频道,您并没有打开一个新连接,您仍然会使用您现在要求订阅私人频道的同一个 Websocket,这是开始连接时发送 cookie 的同一个 websocket。

现在,如果您偶然可以在 websocket-rails gem 上设置断点,而不是文件 dispatcher.rb,在方法 route(event) 上,您会注意到在后台,websocket-rails 使用 faye-websocket-ruby 来处理websocket 连接,并且在请求中,您可以访问在 websocket 握手时发送的 cookie。

该路由会将请求路由到 websocket-rails 控制器 AuthorizationController,在大多数情况下,它的代码如下:

if can?(:subscribe, message[:channel])
  accept_channel current_user
else
  deny_channel({:message => 'authorization failed!'})
end

并且因为它可以访问与通用 Rails 控制器相同的辅助方法,即 cancan?来自 cancan 的方法,将调用 current_user 帮助程序,并且此方法将具有对 cookie 的完全访问权限。

所以channels不是websocket RFC上描述的东西,websocket只是一个可以用来发送任何数据的socket,在这种情况下,公共和私有通道只是作者开发的一种通信行为gem websocket-rails 创建通信渠道并将消息广播到不同的客户端。

如果您进入 websocket-rails 问题,您会注意到甚至有创建单向安全通道的请求:https ://github.com/DanKnox/websocket-rails/issues/52

因此,它不像有人可以发送 subscribe_to_private 频道的流并访问该频道,在他们甚至可以发送订阅流之前,他们必须通过 http 请求创建 websocket 连接,如 rfc 所述,然后,通过该 websocket 连接,他们必须发送字节流,要求 ruby​​ gem 订阅该频道,当他们这样做时,在 rails 服务器上,您可以自动访问连接时发送的该 websocket 的 cookie被创建。

于 2013-03-01T08:06:27.967 回答