ActionCable - новий фреймворк для зв'язку в реальному часу, реалізованому з допомогою протоколу websocket, а також він буде частиною Rails 5. Я не збираюся сильно заглиблюватись у все, ви можете більш детально ознайомитись зі всім, що вас буде цікавити на сторінці проекту за цим посиланням.
Websocket сервер запускається в іншому процесі, ніж головний сервер Rails, тому нам потрібно здійснити автентифікацію користувачів також і тут. В прикладі Девід (розробник ActionCable та контрибютор Rails) використав звичайний метод автентифікації, базований на куках, в самому застосунку, а також ревалідація їх в websocket з'єднанні. Це прекрасно демонструє задумку, але багато із користувачів Rails використовують Devise, тому я хочу з вами поділитись тим, як вирішив цю проблему я.
Websocket сервер не має сесії (session), але може зчитувати ті ж куки з самого застосунку, тому я подумав, що зможу просто взяти куки із user id, та перевірити їй же через socket з'єднання. Щоб це зробити, я використав Warden хук:
# app/config/initializers/warden_hooks.rb
Warden::Manager.after_set_user do |user, auth, opts|
scope = opts[:scope]
auth.cookies.signed["#{scope}.id"] = user.id
end
# 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
logger.add_tags 'ActionCable', current_user.name
end
protected
def find_verified_user
if verified_user = User.find_by(id: cookies.signed['user.id'])
verified_user
else
reject_unauthorized_connection
end
end
end
end
Це все дуже просто і працює, але мені ще необхідні також деякі тайм-аути, а саме щоб завершувати сесію з певним періодом часу, тому я вказав час спливання куки теж:
# app/config/initializers/warden_hooks.rb
Warden::Manager.after_set_user do |user, auth, opts|
scope = opts[:scope]
auth.cookies.signed["#{scope}.id"] = user.id
auth.cookies.signed["#{scope}.expires_at"] = 30.minutes.from_now
end
# app/channels/application_cable/connection.rb
...
protected
def find_verified_user
verified_user = User.find_by(id: cookies.signed['user.id'])
if verified_user && cookies.signed['user.expires_at'] > Time.now
verified_user
else
reject_unauthorized_connection
end
end
....
І ще на кінець нам потрібно зробити куку недійсною, якщо користувач закінчив сесію - вийшов із сайту. Це можна зробити з допомогою іншого Warden хука:
# app/config/initializers/warden_hooks.rb
...
Warden::Manager.before_logout do |user, auth, opts|
scope = opts[:scope]
auth.cookies.signed["#{scope}.id"] = nil
auth.cookies.signed["#{scope}.expires_at"] = nil
end
...
І це все. Тепер я можу ділитись Devise автентифікацією із моїм сервером websocket. Якщо вам цікаво це побачити на прикладі, то можете подивитись на мій форк actioncable-example.
Ще немає коментарів