LoginSignup
0
0

More than 1 year has passed since last update.

Remember_meのチェックをしたときにだけ、指定のURLに遷移しないようにする方法

Posted at

Railsのログイン機能(Device)で、remember_meのチェックをしたときにだけログイン状態が継続するような仕組みを作るために苦戦したので備忘。

やりたいこと
 remember_meのチェックをしたとき、ログインのURLに遷移しないようにする。(ログインユーザの画面に遷移するようにする)
 チェックをしなければ、ログインのURLに遷移するようにする。

やったこと
 Userモデルにremember_meを有効にするコードを追加、更に、remember関数とそれに付随する関数を追加

/rails_projects/dev_obento_01/app/models/user.rb
class User < ActiveRecord::Base
  attr_accessor :remember_token
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable
    has_many :reserves, class_name: 'Reserve'
    validates :user_name, presence: true
    validates :email, presence: true
  # remember_meにチェックを付けた場合は、自動的にログインされる・・・はず
  def remember_me
    true
  end
  
  # 渡された文字列のハッシュ値を返す
  def User.digest(string)
    cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
                                                  BCrypt::Engine.cost
    BCrypt::Password.create(string, cost: cost)
  end

  # ランダムなトークンを返す
  def User.new_token
    SecureRandom.urlsafe_base64
  end

  # 永続セッションのためにユーザーをデータベースに記憶する
  def remember
    self.remember_token = User.new_token
    update_attribute(:remember_digest, User.digest(remember_token))
  end
  
  # 渡されたトークンがダイジェストと一致したらtrueを返す
  def authenticated?(remember_token)
    BCrypt::Password.new(remember_digest).is_password?(remember_token)
  end

  # ユーザーのログイン情報を破棄する
  def forget
    update_attribute(:remember_digest, nil)
  end
  
end

結果、下記のエラーが発生しました。
スクリーンショット 2022-09-27 222502.png
Railsチュートリアルを見ると、「remember_digest属性をUserモデルに追加」する必要があることに気づきました。チュートリアルに沿ってremember_digest属性をUserモデルに追加し、
DeviseのSessionControllerを下記のようにコーディングしました。

/rails_projects/dev_obento_01/app/controllers/users/sessions_controller.rb
# frozen_string_literal: true

class Users::SessionsController < Devise::SessionsController
  include ApplicationHelper
  # GET /resource/sign_in
  #def new
  #end

  # POST /resource/sign_in
  def create
    user = User.find_by(email: params[:session][:email].downcase)
    if user && user.valid_password?(params[:session][:password])
      log_in(user)
      params[:session][:remember_me] == '1' ? remember(user) : forget(user)
      # ログインユーザの遷移先
      if user.admin == true
          redirect_to "/admin/controller02"
      else
          redirect_to "/controller02"
      end
    else
      flash.now[:danger] = 'Invalid email/password combination'
      render 'new'
    end
  end
  
  # DELETE /resource/sign_out
  def destroy
    log_out if logged_in?
    redirect_to "/users/sign_in"
  end

end

しかし、これではremember_meのチェックをしたとしても、再度ログイン画面に遷移してしまいます・・・

ログを確認すると、下記のような挙動となっていました。

Started GET "/users/sign_in" for 113.149.235.78 at 2022-09-28 23:44:14 +0000
Processing by Users::SessionsController#new as HTML
  Rendering layout layouts/application.html.erb
  Rendering devise/sessions/new.html.erb within layouts/application
  Rendered devise/shared/_links.html.erb (Duration: 0.3ms | Allocations: 166)
  Rendered devise/sessions/new.html.erb within layouts/application (Duration: 3.1ms | Allocations: 1066)
  Rendered layout layouts/application.html.erb (Duration: 26.4ms | Allocations: 15373)
Completed 200 OK in 31ms (Views: 29.6ms | Allocations: 16671)

つまり、Users::SessionsControllerのnewメソッドが処理されていることになります。
そこで、Users::SessionsControllerのnewメソッドを追加しました。

/rails_projects/dev_obento_01/app/controllers/users/sessions_controller.rb
class Users::SessionsController < Devise::SessionsController
  include ApplicationHelper
  def new
    #current_userの存在有無で判定
    if current_user
    # ログインユーザの遷移先
      if current_user.admin == true
            redirect_to "/admin/controller02"
      else
            redirect_to "/controller02"
      end
    end
 end
(続く)

これによって、current_userの存在有無とadminか否かで遷移先を変更することができました。
current_userはこちらで定義しております。

/rails_projects/dev_obento_01/app/helpers/application_helper.rb
module ApplicationHelper
  # 渡されたユーザーでログインする
  def log_in(user)
    session[:user_id] = user.id
  end

  # 現在ログイン中のユーザーを返す(いる場合)
  def current_user
    if (user_id = session[:user_id])
      @current_user ||= User.find_by(id: user_id)
    elsif (user_id = cookies.signed[:user_id])
      user = User.find_by(id: user_id)
      if user && user.authenticated?(cookies[:remember_token])
        log_in user
        @current_user = user
      end
    end
  end

  #受け取ったユーザーがログイン中のユーザーと一致すれば true を返す
  def current_user?(user)
    user == current_user
  end

  # ユーザーがログインしていれば true、その他なら false を返す
  def logged_in?
    !current_user.nil?
  end

  # 現在のユーザーをログアウトする
  def log_out
    forget(current_user)
    session.delete(:user_id)
    @current_user = nil
  end
(続く)

しかし、ここで作業しながら別の問題に気づきました。。。
logout時にlog_out関数が呼び出されず、セッションが切れないという問題です。
そこで、改めてUsers::SessionsControllerに下記を追記しました。

/rails_projects/dev_obento_01/app/controllers/users/sessions_controller.rb
class Users::SessionsController < Devise::SessionsController
  include ApplicationHelper
  #↓ここを追記
  skip_before_action :verify_signed_out_user, raise: false
(続く)

さて、本題に戻りまして、
これでもremember_meをチェックしなくても、ログインユーザ指定の画面に遷移してしまいます。。。
何が問題か2日かけて考え、これで何とか実装できるようになりました!

/rails_projects/dev_obento_01/app/controllers/users/sessions_controller.rb
class Users::SessionsController < Devise::SessionsController
  #before_action :configure_sign_in_params, only: [:create]
  include ApplicationHelper
  skip_before_action :verify_signed_out_user, raise: false
  
  def new
    #↓current_userの情報をcookieで判定(ここが足りなかった!)
    if current_user && current_user.authenticated?(cookies[:remember_token])
    
  # ログインユーザの遷移先
      if current_user.admin == true
            redirect_to "/admin/controller02"
      else
   (続く)

cookieとセッションの違いについてはまだはっきりと分かっていないのですが、
参考にしたページ(ベースはRails tutorial第9章)を読み解くと、
Remember meのチェックしたか否かでcookieを保持するか、削除するかという挙動の違いがあることが分かりました。ですので、そのcookieを保持しているかの条件式を指定したい場所のコードに書けば良かった、ということでした!

結論
 remember_digestを用いてRemember_meのチェックをしたときにだけ、指定のURLに遷移しないようにする方法としては、cookieの削除がされたか否かで遷移先を判定する方法がある。(セッションが削除されたか否かではない)

参考URL
https://railstutorial.jp/chapters/advanced_login?version=5.1
https://teratail.com/questions/245600
https://qiita.com/th_9plus/questions/e8365ce6b705399ea0d5
https://qiita.com/d0ne1s/items/f73e1725cc214af82587
https://qiita.com/dany1468/items/692fc51d2be58ddb857a
https://rennnosukesann.hatenablog.com/entry/2018/02/17/225921

0
0
0

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
0
0