はじめに
ActionCableを利用して、チャンネル接続/購読時にユーザ認証を行う方法と、認証に成功/失敗した時にクライアント(JavaScript)側で呼び出されるfunctionについて確認した結果について投稿させていただきます。
投稿時のバージョンはRails 5.2.0です。
ActionCableの動作確認用に作成したアプリのソースコードはこちらに配置してあります。
NaokiIshimura/action-cable-example
前提条件
想定サービス
- 複数のチャットルームが作成できるチャットサービス
- ログインしないとサービスは利用できない
- 許可されたユーザのみチャットルームに入室できる
クライアント(JavaScript)側の実装
var channel = "ChatChannel"; // チャンネル名
var room = "1"; // チャットルームID
App.cable.subscriptions.create({channel: channel, room: room}, {
// サブスクリプションがサーバー側で利用可能になると呼び出される
connected: function () {
//...
},
// WebSocket接続が閉じると呼び出される
disconnected: function () {
//...
},
// サブスクリプションがサーバーに拒否されると呼び出される
rejected: function () {
//...
},
//...
});
Cookieへのユーザ識別子(user_id)保存
Websocketサーバからはセッションにはアクセスできないが、Cookieにはアクセスできるため、ログイン成功時にCookieへuser_idを保存するようにしておく。
# Cookieにuser_idを署名付きで保存する
cookies.encrypted[:user_id] = user.id
Userモデルへのメソッド追加
ユーザがチャットルームへアクセスにアクセスできるか確認するメソッドをUserモデルに追記しておく。
class User < ApplicationRecord
def can_access?(room)
if # チャットルームへ入室するための条件を記述
true
else
false
end
end
end
ActionCableの実装
WebSocket接続時の認証
サンプルコードがAction Cable Overview — Ruby on Rails Guidesに紹介されていたので、そのまま利用した。
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
if current_user = User.find_by(id: cookies.encrypted[:user_id])
current_user
else
reject_unauthorized_connection
end
end
end
end
チャンネル購読時の認証
サンプルコードがhttp://api.rubyonrails.org/|ActionCable::Channel::Baseに記載されていたので参考にした。
class ChatChannel < ApplicationCable::Channel
def subscribed
room = Chat.find(params['room'])
if current_user.can_access?(room)
stream_from "messages_#{params['room']}_channel"
else
reject
end
end
end
結果
- チャンネル接続/購読時の認証に成功するとjsでconnectedが呼び出される
- チャンネル接続時の認証に失敗するとjsでdisconnectedが呼び出される
- チャンネル接続時の認証に成功しても、購読時の認証に失敗するとjsでrejectedが呼び出される
チャンネル接続時の認証 | チャンネル購読時の認証 | jsで呼び出されるfunction |
---|---|---|
成功 | 成功 | connected |
成功 | 失敗 | rejected |
失敗 | 成功 | disconnected |
失敗 | 失敗 | disconnected |
補足
本投稿時点では英語版と日本語版のRailsガイドでサンプルコード内のCookieの保存方法に差分がある。
英語版では「暗号化Cookie(cookies.encrypted)」日本語版では「署名付きCookie(cookies.signed)」へユーザ識別子(user_id)を保存するようにしている。