Как получить current_user в ActionCable rails-5-api app?
Почему мне не удается получить current_user
внутри моего канала или как мне получить current_user
?
Что я использую?
- Rails 5.0.1 --api (у меня нет ни одного вида NOR, использующего кофе)
- Я использую адаптивное приложение для тестирования этого (отлично работает БЕЗ авторизации)
- Я НЕ использую devise для auth (вместо этого я использую JWT, используя Knock, поэтому нет файлов cookie)
Попытка получить current_user
внутри моего канала ActionCable, как описано в rubydoc.info
Код выглядит как
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>]
Ответы
Ответ 1
Если вы видите предоставленный документ, вы узнаете, что identified_by
не является методом для экземпляра Channel
. Это метод для Actioncable::Connection
.
Из руководства Rails для обзора Actioncable это выглядит как класс 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 после аутентификации.
Ответ 2
Спасибо @Sajan, за то, что кикнул меня по правильному пути...
Я создал репо action-cable-react-jwt, который точно делает именно то, что я задал в вопросе: Rails, React- Родной и JWT.
Поскольку запрос websocket таинственным образом не позволял отправлять настраиваемые заголовки, я просто сделал грязный взлом для аутентификации JWT (добавив JWT в существующий заголовок: D)
Ответ 3
если вы используете разработанные драгоценные камни в рельсах, пожалуйста, замените эту функцию:
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
Я надеюсь, что это поможет вам.
Ответ 4
Ну по идее
- У вас есть доступ к файлам cookie в классе
ActiveCable::Connection
. - Вы можете устанавливать и получать
cookies.signed
и cookies.encrypted
- И приложение, и ActionCable используют одну и ту же конфигурацию, поэтому они используют одну и ту же "secret_key_base"
Итак, если вы знаете имя вашего сеансового куки (как-то очевидно, пусть он будет называться " _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