【问题标题】:Rails: Action Cable: How to authorize user to connect specific channel based on role?Rails:Action Cable:如何根据角色授权用户连接特定频道?
【发布时间】:2018-11-28 20:26:41
【问题描述】:

在我的 Rails 拍卖应用中,授权用户可以在产品页面内同时连接到 2 个频道(一个是产品的 all_users 频道​​,另一个是用于直接消息传递的用户特定频道。)

现在我只想将敏感信息发送给管理员组用户。我认为我可以在咖啡脚本中定义第三个通道连接请求(admin_channel),但我不知道如何根据角色授权第三个通道的用户连接。

另一种选择可能是利用现有的用户特定频道,但在这里我无法弄清楚后端类如何知道管理员组中的哪些用户当前在线(有一个用户频道启动和运行)..

您知道我该如何实现吗?任何形式的支持将不胜感激..

您可以在下面找到我现有的 connection.rb 文件和 coffeescript 文件。

这是我的 connection.rb 文件:

module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_user


    def connect
      self.current_user = find_verified_user
    end

    protected

    def find_verified_user # this checks whether a user is authenticated with devise
      if verified_user = env['warden'].user
        verified_user
      else
        reject_unauthorized_connection
      end
    end
  end
end

咖啡脚本:

$( document ).ready ->
    App.myauction = App.cable.subscriptions.create({
      channel: 'MyauctionChannel'
      id: $('#auctionID').attr('data-id')
      },
      connected: ->
        console.log "Connected"
        # Called when the subscription is ready for use on the server

      disconnected: ->
        # Called when the subscription has been terminated by the server

      speak: (message) ->
        @perform 'speak', message: message

      received: (data) ->
        console.log(data)
        # Called when there's incoming data on the websocket for this channel
    )

    App.myauctionuser = App.cable.subscriptions.create({
      channel: 'MyauctionChannel',
      id: $('#auctionID').attr('data-uuid-code')
      },
      connected: ->
        console.log "user connected"
        # Called when the subscription is ready for use on the server

      disconnected: ->
        # Called when the subscription has been terminated by the server

      speak: (message) ->
        @perform 'speak', message: message

      received: (data) ->
        # console.log ("user channel ")
        # console.log(data)
    )

【问题讨论】:

  • 他们是否连接到同一个频道MyauctionChannel?但是他们连接到不同的房间,如您的id: $('#auctionID').attr('data-id') 所标识,而另一个是id: $('#auctionID').attr('data-uuid-code')
  • @Jay-Ar Polidario 我试图对问题进行更多澄清。如您所述,用户连接到基于产品的“广播”频道 $('#auctionID').attr('data-uuid -code') 以及用户特定的频道。

标签: ruby-on-rails actioncable


【解决方案1】:
$(document).ready ->
    App.privateAdminMesssagesChannel = App.cable.subscriptions.create({
        channel: 'PrivateAdminMessagesChannel'
      },
      connected: ->
      disconnected: ->
      // call this function to send a message from a Non-Admin to All Admins
      sendMessageToAdmins: (message) ->
        @perform 'send_messsage_to_admins', message: message
      // call this function to send a messsage from an Admin to (a Non-admin + all Admins)
      sendMessageToUserAndAdmins: (message, toUserId) ->
        @perform 'send_messsage_to_user_and_admins', message: message, to_user_id: toUserId

      received: (data) ->
        console.log(data.from_user_id)
        console.log(data.to_user_id)
        console.log(data.message)

        if data.to_user_id
          // this means the message was sent from an Admin to (a Non-admin + all admins)
        else
          // this means the message was sent from a Non-admin to All Admins
          // do some logic here i.e. if current user is an admin, open up one Chatbox
          // on the page for each unique `from_user_id`, and put data.message
          // in that box accordingly
    )

private_admin_messages_channel.rb

class PrivateAdminMessagesChannel < ActionCable::Channel::Base
  def subscribed    
    stream_from :private_admin_messages_channel, coder: ActiveSupport::JSON do |data|
      from_user = User.find(data.fetch('from_user_id'))
      to_user = User.find(data['to_user_id']) if data['to_user_id']
      message = data.fetch('message')

      # authorize if "message" is sent to you (a non-admin), and also
      # authorize if "message" is sent to you (an admin)

      if (to_user && to_user == current_user) || (!to_user && current_user.is_admin?)
        # now, finally send the Hash data below and transmit it to the client to be received in the JS-side "received(data)" callback
        transmit(
          from_user_id: from_user.id,
          to_user_id: to_user&.id,
          message: message
        )
      end
    end
  end

  def send_message_to_admins(data)
    ActionCable.server.broadcast 'private_admin_messages_channel', 
      from_user_id: current_user.id,
      message: data.fetch('message')
  end

  def send_message_to_user_and_admins(data)
    from_user = current_user

    reject unless from_user.is_admin?

    ActionCable.server.broadcast 'private_admin_messages_channel', 
      from_user_id: from_user.id,
      to_user_id: data.fetch('to_user_id'),
      message: data.fetch('message')
  end
end

以上是我能想到的最简单的方法。不是最有效的,因为每个流都会发生额外级别的授权(请参阅stream_from 块内部),这与我们有不同的广播名称不同,其中授权只会在“连接”本身发生一次,而不是每个“流”...这可以通过以下方式完成:

  1. Admin User1 打开页面然后 JS 订阅 UserConnectedChannel
  2. 非管理员用户2打开页面然后JS订阅PrivateAdminMessagesChannel传入数据:user_id: CURRENT_USER_ID
  3. 从上面的 2. 开始,因为 User2 刚刚订阅;然后在后端,在def subscribed 内连接后,你ActionCable.server.broadcast :user_connected, { user_id: current_user.id }
  4. Admin User1 订阅UserConnectedChannel,然后接收data { user_id: THAT_USER2_id }
  5. 从上面的 4 开始,在 JS received(data) 回调中,您现在 JS 订阅 PrivateAdminMessagesChannel 传入数据:THAT_USER2_id`。
  6. 现在User1和User2都订阅了PrivateAdminMessagesChannel user_id: THAT_USER2_id,这意味着他们可以私下交谈(其他管理员应该也收到了:user_connected的JS数据:{ user_id: THAT_USER2_ID },所以他们也应该是也订阅了,因为 AdminUser1、NonAdminUser2 和 AdminUser3 可以在同一个聊天频道中交谈是有道理的...根据您的要求)
  7. TODO:从上面的 1 到 6,也用“断开”过程做类似的事情

琐事:

  • 您在ApplicationCable::Connection 中使用identified_by 定义的内容可以在您的频道文件中访问。特别是在这种情况下,可以调用current_user
  • 关于拒绝订阅,请参阅docs here

【讨论】:

  • 我没有测试过这个,所以我可能错过了一些东西;让我相应地知道
  • @Tolga 不用担心 祝你好运! :)
猜你喜欢
  • 2020-06-08
  • 1970-01-01
  • 1970-01-01
  • 2021-01-31
  • 2021-08-30
  • 1970-01-01
  • 2017-08-05
  • 2011-09-29
  • 1970-01-01
相关资源
最近更新 更多