#まえがき
リアルタイムWebアプリケーションを開発していると、channel内でログイン情報を扱いたくなる時がよくあると思います。
ActionCable内でセッションストアをCookieStoreにした場合の
ログイン情報の取得例はいくつかサンプルがありましたが、
セッションストアにRedisを利用している場合のサンプルがなかったのでまとめておきます。
#環境
Ruby: 2.3.1
Rails: 5.0.0.1
で試しています。
RailsのセッションストアをRedisにする
まずはセッションストアをredisにする為に以下のGemをインストールします。
また、Redisを扱いやすくするためにredis-namespaceも利用しています。
gem 'redis'
gem 'redis-rails'
gem 'redis-namespace'
続いて以下のファイルを設定します。
Rails.application.config.session_store :redis_store, {
servers: {
host: 'localhost',
post: 6379,
db: 0,
namespace: 'sessions'
},
key: 'sessions',
expire_after: 1.day
}
ユーザーデータをcookieに書き込む
ログイン認証部分でsessionにデータをセットしたあとにCookieにuser_idをいれておきます。
def login
# ユーザーデータをセット
session[:user_id] = 1
session[:nickname] = "test"
# signed cookieにuser_idを持っておく
cookies.signed[:user_id] = user["user_id"]
end
redis側の設定
development:
hosts:
- localhost
db:
session: 0
test:
hosts:
- localhost
db:
session: 0
production:
hosts:
- <%= ENV['REDIS_HOST'] %>
db:
session: 0
ActionCable内でユーザーログイン状態を取得する
ActionCable内では通常のsession
は利用できません。
そのため、WebSocket経由で送られてくるCookieからuser_idを特定し、session_dataを取得する必要があります。
ApplicationCable内ではidentified_by
で指定した値でユーザーを識別することができます。
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user
def connect
val = session_from_redis
if val
self.current_user = {
user_id: val["user_id"],
nickname: val["nickname"]
}
end
end
def session_from_redis
# redisのsessionのnamespaceを取得
redis_namespace = Rails.application.config.session_options[:servers][:namespace]
# session cookieの名前を取得
cookie_key = cookies[Rails.application.config.session_options[:key]]
# redis内にあるsession_keyを組み立てる
session_key = "#{redis_namespace}:#{cookie_key}"
# redisからsessionを取得
redis = CustomRedis.new({'db' => 'session'})
val = redis.get(session_key)
# Marshalされているのでdecode
val ? Marshal.load(val) : nil
end
end
end
ユーザーデータの参照
channel内でもcurrent_user
からユーザーデータを取得することが可能です
class RoomChannel < ApplicationCable::Channel
def subscribed
stream_from "room_channel"
end
def unsubscribed
# Any cleanup needed when channel is unsubscribed
end
def speak(data)
ai = AI::Engine.new(user_id: current_user[:user_id], nickname: current_user[:nickname])
ai.trigger(data['message'])
end
end
#まとめ
実際に運用する場合は細かいエラー処理やセキュリティ対策などが必要ですが、簡易的に保存&取得したい場合はこのような手法でログイン情報をやり取りすることができます。