チャネル内でcurrent_user
を取得できないのはなぜですか、またはcurrent_user
を取得するにはどうすればよいですか?
何を使いますか?
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_by
がChannel
インスタンスのメソッドではないことがわかります。 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を保存する必要があると思います。
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
これがあなたの助けになることを願っています。
まあ、理論的には:
ActiveCable::Connection
クラスのCookieにアクセスできます。cookies.signed
とcookies.encrypted
を設定して受信できますしたがって、セッション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
self.current_user
でApplicationCable::Connection
を設定すると、チャネルインスタンスで使用できるようになります。したがって、Sajanが書いたように認証を設定し、MessageChannel
でcurrent_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
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