LoginSignup
401
422

More than 1 year has passed since last update.

簡単ログイン・ゲストログイン機能の実装方法(ポートフォリオ用)

Last updated at Posted at 2019-10-04

ポートフォリオ用のWebサイトに必須と言われるゲストログイン機能

特にDeviseconfirmableを使用している場合は,ゲストログイン機能が無いと,新規登録が面倒という理由でポートフォリオを見てすらもらえない可能性が高くなります。

ところが,普通のWebサイトには導入しないためか,記事が見当たりませんでした。また,ゲストユーザー機能が動作しなくなるリスクを抱えた実装も複数見かけました。そこで,私のたどりついた実装方法をアップさせていただきます。

以下,Deviseを使用している前提とし,トップページが root 'homes#index' で設定されていることとします。

routes.rb
Rails.application.routes.draw do
  # トップページは homes#index の前提
  root "homes#index"
end

YouTube

その1: ゲストログイン機能の実装方法(簡易版)

config/routes.rb
# 以下を追加
  post '/homes/guest_sign_in', to: 'homes#guest_sign_in'
app/controllers/homes_controller.rb
# 以下を追加
  def guest_sign_in
    user = User.find_or_create_by!(email: 'guest@example.com') do |user|
      user.password = SecureRandom.urlsafe_base64
      # user.skip_confirmation!  # Confirmable を使用している場合は必要
      # 例えば name を入力必須としているならば, user.name = "ゲスト" なども必要
    end
    sign_in user
    redirect_to root_path, notice: 'ゲストユーザーとしてログインしました。'
  end
app/views/homes/index.html.erb
# 以下を追加
<%= link_to 'ゲストログイン(閲覧用)', homes_guest_sign_in_path, method: :post %>
  • ポイントはDevisesign_inメソッドを利用することです。

  • find_byではなく,find_or_create_byを利用しています。これにより,ゲストユーザーをあらかじめ作成する手間を省けます。また,ゲストユーザーを削除されてゲスト機能が動作しなくなるリスクも回避できます。

  • パスワードを特定されると,ユーザー編集ページからメールアドレスパスワードを変更される可能性があるため,パスワードはランダム文字列にしています。

  • バリデーションの影響でゲストユーザーを作成できない場合は,エラーを発生させるように設定しております。

その2: ゲストログイン機能の実装方法

上記の実装方法は理解しやすいものの,ゲストログイン機能をHomesControllerに任せることには違和感があります。本来は,ログイン機能同様,DeviseSessionsControllerに任せるべきでしょう。

例えば次のように実装すると,より自然なものになるのではないでしょうか。

2-1. ゲストログイン機能の設定

  • まず,SessionsControllerに新しいアクションguest_sign_inを準備します。
config/routes.rb
# 以下を追加
  devise_scope :user do
    post 'users/guest_sign_in', to: 'users/sessions#guest_sign_in'
  end
  • アクションguest_sign_inを設定するため,app/controllersusersディレクトリを作成し,その中に次のsessions_controller.rbを作成します。ゲストユーザーを探す or 作成する機能は User.rb に移動させるとコントローラーがスッキリします。
app/controllers/users/sessions_controller.rb
class Users::SessionsController < Devise::SessionsController
  def guest_sign_in
    user = User.guest
    sign_in user
    redirect_to root_path, notice: 'ゲストユーザーとしてログインしました。'
  end
end
app/models/user.rb
# 以下を追加
  def self.guest
    find_or_create_by!(email: 'guest@example.com') do |user|
      user.password = SecureRandom.urlsafe_base64
      # user.confirmed_at = Time.now  # Confirmable を使用している場合は必要
      # 例えば name を入力必須としているならば, user.name = "ゲスト" なども必要
    end
  end
  • トップページにゲストログインボタンを用意する場合は,以下を追加すればOKです。(スタイルは各自で追加して下さい)
app/views/homes/index.html.erb
# 以下を追加
<%= link_to 'ゲストログイン(閲覧用)', users_guest_sign_in_path, method: :post %>

2-2. ゲストユーザーを削除できないようにする

上記の実装方法ならば,仮にゲストユーザーを削除されたとしても,ゲスト機能が動作しなくなることはありません。

ですが,例えば2名の方が同時にログインされている状態で,片方の方がゲストユーザーを削除しますと,もう片方の方も強制的にログアウトさせられてしまいます。ポートフォリオの場合はレアケースだと思いますが,念のためゲストユーザーを削除できないように設定しておきましょう。

  • ゲストユーザーが削除機能を使用できないようにするには,registrations.rbを編集する必要があります。まずは,ルーティングを変更します。
config/routes.rb
# devise_for :users を次に置き換える
  devise_for :users, controllers: {
    registrations: 'users/registrations'
  }
  • destroyアクションの動作前に,メールアドレスがゲストユーザー用になっていないかチェックするように設定します。
    • ゲストユーザーならばフラッシュを出した上でトップページにリダイレクトさせるように設定しています。
app/controllers/users/registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
  before_action :ensure_normal_user, only: :destroy

  def ensure_normal_user
    if resource.email == 'guest@example.com'
      redirect_to root_path, alert: 'ゲストユーザーは削除できません。'
    end
  end

end

2-3 ゲストユーザーがメールアドレス・パスワードを変更できないようにする

上記の実装ならば,「ユーザー編集機能」や「パスワード再設定機能」によりメールアドレスパスワードを変更される可能性は非常に低いですし,仮に変更されたとしてもポートフォリオならば問題にならないかと思います。

それでも,「ゲストユーザーのメールアドレス・パスワードを絶対に変更されたくない!」という場合は,更に次のような設定をすればOKです。

  • 削除機能を止めるのと同じ手法で,ゲストユーザーがメールアドレス・パスワードを編集できないように設定します。
app/controllers/users/registrations_controller.rb
- before_action :ensure_normal_user, only: :destroy
+ before_action :ensure_normal_user, only: %i[update destroy]


- redirect_to root_path, alert: 'ゲストユーザーは削除できません。'
+ redirect_to root_path, alert: 'ゲストユーザーの更新・削除はできません。'
  • パスワード再設定メールの送信機能を止めるには,passwords_controller.rbcreateアクションの動作前にチェックすればOKです。まずはルーティングを変更します。
config/routes.rb
#   devise_for :users, controllers: {
#     registrations: 'users/registrations'
#   }
# を次に置き換える。(,の付け忘れに注意!)
  devise_for :users, controllers: {
    registrations: 'users/registrations',
    passwords: 'users/passwords'
  }
  • パスワード再設定ページのフォームに入力されたメールアドレスはparams[:user][:email]で受け取れるので,これを利用してゲストユーザーを特定します。
    • メールアドレスは大文字が小文字に変換されて保存されているため,downcaseメソッドが必要です。
app/controllers/users/passwords_controller.rb
class Users::PasswordsController < Devise::PasswordsController
  before_action :ensure_normal_user, only: :create

  def ensure_normal_user
    if params[:user][:email].downcase == 'guest@example.com'
      redirect_to new_user_session_path, alert: 'ゲストユーザーのパスワード再設定はできません。'
    end
  end
end

備考

  • 上記はあくまで最低限度の実装です。例えば,ゲストユーザーの情報は他の利用者にも引き継がれますので,必要があればTrackableを入れて,current_sign_in_atを基準に次のような実装をしてもよいかもしれません。

    • 定期的にデータを初期化する設定を入れる
    • ゲストユーザーを複数用意して,現在ログイン日時の最も古いゲストユーザーでログインする
  • 実は,最初,devise/sessions/new.html.erbの真似をして,hidden_fieldを加えて……という手順で実装しようとしました。ところが,この方法ではuser_email, user_passwordなどのidセレクタが重複するエラーが出てしまいます。そこで,sign_inメソッドの存在を思い出し,このような実装方法にたどりつきました。

401
422
7

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
401
422