web-dev-qa-db-ja.com

ActionCable Rails-5-apiアプリでcurrent_userを取得するにはどうすればよいですか?

チャネル内でcurrent_userを取得できないのはなぜですか、またはcurrent_userを取得するにはどうすればよいですか?

何を使いますか?

  • Rails 5.0.1 --api(ビューがありませんNORコーヒーを使用)
  • 私はこれをテストするためにreact-nativeアプリを使用しています(認証なしで正常に動作します)
  • 私は認証にdeviseを使用しません(代わりにKnockを使用するのでCookieは使用しません)

rubydoc.info で説明されているように、ActionCableチャネル内でcurrent_userを取得しようとしています

コードは次のようになります

class MessageChannel < ApplicationCable::Channel
  identified_by :current_user

  def subscribed
    stream_from 'message_' + find_current_user_privileges
  end

  def unsubscribed
    # Any cleanup needed when channel is unsubscribed
  end

  protected

  def find_current_user_privileges
    if current_user.has_role? :admin
      'admin'
    else
      'user_' + current_user.id
    end
  end

end

そしてそれを実行すると、私はこのエラーを受け取ります:

[NoMethodError - undefined method `identified_by' for MessageChannel:Class]

そして、identified_by :current_userを削除すると、

[NameError - undefined local variable or method `current_user' for #<MessageChannel:0x7ace398>]

提供したドキュメントを見ると、identified_byChannelインスタンスのメソッドではないことがわかります。 Actioncable::Connectionのメソッドです。 Rails Actioncable Overviewのガイド)から、Connectionクラスは次のようになります。

#app/channels/application_cable/connection.rb
module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_user

    def connect
      self.current_user = find_verified_user
    end

    private
      def find_verified_user
        if current_user = User.find_by(id: cookies.signed[:user_id])
          current_user
        else
          reject_unauthorized_connection
        end
      end
  end
end

ご覧のとおり、ここではcurrent_userは利用できません。代わりに、ここに関連してcurrent_userを作成する必要があります。

WebSocketサーバーにはセッションがありませんが、メインアプリと同じCookieを読み取ることができます。したがって、認証後にCookieを保存する必要があると思います。

6
Sajan

railsでデバイスgemを使用している場合は、次の関数を置き換えてください。

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

これがあなたの助けになることを願っています。

3
Sochetra Nov

まあ、理論的には:

  • ActiveCable::ConnectionクラスのCookieにアクセスできます。
  • cookies.signedcookies.encryptedを設定して受信できます
  • アプリケーションとActionCableはどちらも同じ構成を共有するため、同じ「secret_key_base」を共有します。

したがって、セッションCookieの名前がわかっている場合(どういうわけか明らかですが、「_session」と呼びます)、次の方法でデータを受け取ることができます。

cookies.encrypted['_session']

したがって、次のようなことができるはずです。

user_id = cookies.encrypted['_session']['user_id']

これは、セッションにCookieストアを使用するかどうか、および正確な認証アプローチに依存しますが、いずれの場合も、必要なデータがそこにある必要があります。

セッションは使用する認証ソリューションによってすでに管理されており、Cookieの有効期限や認証ロジックの重複などを気にする必要がないため、このアプローチの方が便利だと思いました。

より完全な例を次に示します。

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

    def connect
      session = cookies.encrypted['_session']
      user_id = session['user_id'] if session.present?

      self.current_user = (user_id.present? && User.find_by(id: user_id))

      reject_unauthorized_connection unless current_user
    end
  end
end

1
Not Entered

self.current_userApplicationCable::Connectionを設定すると、チャネルインスタンスで使用できるようになります。したがって、Sajanが書いたように認証を設定し、MessageChannelcurrent_userを使用することができます。

たとえば、このコードは私のために働いた

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

    def connect
      self.verified_user = find_verified_user
    end

    private

    def current_user
      jwt = cookies.signed[:jwt]
      return unless jwt

      decoded = JwtDecodingService.new(jwt).decrypt!
      @current_user ||= User.find(decoded['sub']['user_id'])
    end

    def find_verified_user
      current_user || reject_unauthorized_connection
    end
  end
end

class NextFeaturedPlaylistChannel < ApplicationCable::Channel
  def subscribed
    stream_from "next_featured_playlist_#{verified_user.id}"
  end
end

0
Axife

Rails 5 APIモードの場合:

application_controller.rb

class ApplicationController < ActionController::API
   include ActionController::Cookies
   ...
   token = request.headers["Authorization"].to_s
   user = User.find_by(authentication_token: token)
   cookies.signed[:user_id] = user.try(:id)

connection.rb

class Connection < ActionCable::Connection::Base
   include ActionController::Cookies
   ...
   if cookies.signed[:user_id] && current_user = User.where(id: cookies.signed[:user_id]).last
     current_user
   else
     reject_unauthorized_connection
   end

config/application.rb

config.middleware.use ActionDispatch::Cookies
0
Abel