簡體   English   中英

Rails:操作電纜:如何根據角色授權用戶連接特定的通道?

[英]Rails: Action Cable: How to authorize user to connect specific channel based on role?

在我的Rails拍賣應用中,授權用戶可以在產品頁面中同時連接到2個通道(一個是產品的all_users通道,另一個是用於直接消息傳遞的用戶特定通道。)

現在,我只想向管理員組用戶發送敏感信息。 我可以在coffee腳本中定義第三個通道連接請求(admin_channel),但我不知道如何根據角色授權第三通道的用戶連接。

另一個選擇可能是利用現有的特定於用戶的渠道,但是在這里我無法弄清楚后端類如何知道admin組中的哪些用戶當前處於聯機狀態(具有正在運行的用戶渠道)。

您有什么想法可以實現嗎? 任何形式的支持將不勝感激。

在下面可以找到我現有的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)
    )
$(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. 管理員User1打開頁面,然后JS訂閱到UserConnectedChannel
  2. 非管理員User2打開頁面,然后JS訂閱到PrivateAdminMessagesChannel傳入數據: user_id: CURRENT_USER_ID
  3. 從上面的2.開始,因為User2剛剛訂閱; 然后在后端,在連接內def subscribed ,您ActionCable.server.broadcast :user_connected, { user_id: current_user.id }
  4. 訂閱UserConnectedChannel管理員User1然后接收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 ,這意味着它們可以彼此PrivateAdminMessagesChannel user_id: THAT_USER2_id (其他管理員也應該已經收到:user_connected的JS數據: { user_id: THAT_USER2_ID } ,因此也應該訂閱它們同樣,因為AdminUser1,NonAdminUser2和AdminUser3可以在同一個聊天通道中進行交談是合理的……根據我的要求,
  7. 待辦事項:從上面的1到6,也可以對“斷開連接”過程執行類似的操作

Trivias:

  • 您在ApplicationCable::Connection使用identified_by定義的那些可以在您的通道文件中使用。 特別是在這種情況下,可以調用current_user
  • 關於拒絕訂閱,請參閱此處的文檔

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM