22
26

More than 5 years have passed since last update.

【ActionCable】チャンネル接続/購読時にユーザ認証を行う

Last updated at Posted at 2018-05-03

はじめに

ActionCableを利用して、チャンネル接続/購読時にユーザ認証を行う方法と、認証に成功/失敗した時にクライアント(JavaScript)側で呼び出されるfunctionについて確認した結果について投稿させていただきます。

投稿時のバージョンはRails 5.2.0です。

ActionCableの動作確認用に作成したアプリのソースコードはこちらに配置してあります。
NaokiIshimura/action-cable-example

前提条件

想定サービス

  • 複数のチャットルームが作成できるチャットサービス
  • ログインしないとサービスは利用できない
  • 許可されたユーザのみチャットルームに入室できる

クライアント(JavaScript)側の実装

app/assets/javascripts/channels/chat.js
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を保存するようにしておく。

app/controllers/sessions_controller.rb
# Cookieにuser_idを署名付きで保存する
cookies.encrypted[:user_id] = user.id

Userモデルへのメソッド追加

ユーザがチャットルームへアクセスにアクセスできるか確認するメソッドをUserモデルに追記しておく。

app/models/user.rb
class User < ApplicationRecord

  def can_access?(room)
    if # チャットルームへ入室するための条件を記述
      true
    else
      false
    end
  end

end

ActionCableの実装

WebSocket接続時の認証

サンプルコードがAction Cable Overview — Ruby on Rails Guidesに紹介されていたので、そのまま利用した。

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

    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に記載されていたので参考にした。

app/channels/chat_channel.rb
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)を保存するようにしている。

参考

22
26
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
26