1

关于websocket-rails的wiki,它有以下使用CanCan宝石的例子。websocket-rails gem and authentication

这是如何工作的?通过一个普通的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

查看https://developer.mozilla.org/en-US/docs/WebSockets/Writing_WebSocket_client_applications,看起来gem作者期望的行为不可靠或通用。 – bdares 2013-02-26 06:56:49

+0

@bdares你是什么意思? – MhdSyrwan 2013-03-26 22:00:16

回答

5

它不完全是原始数据,其实现这一kepts连接打开,如果你检查wikipedia的文件,在握手请求正在创建连接时套接字HTTP协议的升级,有从客户端发送的信息流,以及来自服务器的答案。

所以一个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请求,不过,

如果您在上JS运行代码查看与轨道-的WebSocket创建的请求,

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 

这样cookies被发送,以及最重要的,你的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方法,你会看到,你把所有的就好像它是一个普通的http请求一样。

有关WebSocket的工作原理和方式的更多信息,您可以阅读RFC,但重要的是,在创建连接时的握手过程中,客户端会将Cookie与其他信息一起发送某种类型的http请求,服务器接收到websocket请求,通过检查cookie来验证用户身份,并且打开连接保持打开状态作为全双工通信的套接字,或关闭连接,因为凭证无效。


我不知道我是否理解你的问题。一个普通的页面请求和一个web套接字的主要区别在于,一个web套接字是一个套接字,用户使用http握手来启动连接。

在您的代码上,这些行将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'); 

客户端是要求服务器(在还活着,在网页套接字连接)订阅的频道是私有的。

现在,需要了解的最重要的事情是,如果您订阅了一个频道,但您不打开新的连接,您仍然会使用相同的WebSocket,现在您要求订阅私人频道,并且这是您在开始连接时发送Cookie的websocket。

现在,如果碰巧你可以在websocket-rails gem上设置一个断点,在方法route(event)上没有文件dispatcher.rb,你会注意到在引擎盖下,websocket-rails使用faye-websocket -ruby来处理websocket连接,并且在请求中,你可以访问通过websocket握手发送的cookie。

的路由将请求路由到WebSocket的护栏控制器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护栏的问题,你会发现,甚至有请求创建单向担保渠道:https://github.com/DanKnox/websocket-rails/issues/52

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

+0

但是在websocket使用新的WebSocketRails('localhost:3000/websocket')创建后''当一个频道订阅了'dispatcher.subscribe_private('channel_name')'时发生了什么。 'subscribe_private'在'new WebSocketRails'创建websocket之后,因此'subscribe_private'不能是http请求,所以不能有cookie。我用一个更易读的例子编辑了我的文章。 – user782220 2013-03-01 12:36:50

+0

我更新了答案。当subscribe_private被调用时,它会通过websocket连接发送一块数据,说:“嘿,我想订阅这个信息通道”,rails可以随时通过websocket访问原始的http头文件。它不像有人可以去建立一个websocket连接而获得订阅。理解频道与websocket无关是重要的,这只是在websocket上创建ruby作者,所以它是管理消息广播的一种方式。 – rorra 2013-03-02 09:53:32

+0

因为Rails通常会在每个http请求和无状态http请求之间获取cookie,所以我不希望Rails能够默认持久保存cookie。这是否意味着为了在创建WebSocket之后坚持cookie,websocket-rails gem是猴子补丁Rails使它能够保存cookie以进行WebSocket连接?我真的很想知道如何实现cookie的长期保存。你知道这个代码在哪里实现吗? – user782220 2013-03-02 12:20:23