websocket-rails gem和身份验证

时间:2013-02-24 06:25:57

标签: ruby-on-rails ruby-on-rails-3 ruby-on-rails-3.2 websocket

在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: websocket的http请求以及Cookie。但是在下一行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');

1 个答案:

答案 0 :(得分: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,在方法路由(事件)上,你会发现在引擎盖下,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

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

因此,渠道不是websocket RFC中描述的内容,websocket只是一个可用于发送任何数据的套接字,在这种情况下,公共和私有渠道只是由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连接创建时。