Railsにdeviseを使ってログイン機能を実装する

前回の記事ではRailsアプリにdevise gemをインストールしてセットアップしました。 今回は実際に認証機能付きモデルを追加し、ログイン機能を実装します。

実装は下記の手順で行います。

  1. 認証機能付きモデルの作成
  2. 認証機能用ビューの作成
  3. 認証機能用コントローラの作成
  4. ユーザ名表示、ログアウトボタン設置
  5. その他の追加設定
    • 未ログインでログインページ以外を開けないようにする
    • ログイン後のリダイレクト先を指定する

1. 認証機能付きモデルの作成

rails g devise モデル名単数形 で認証機能を追加するmodelを作成できます。 今回はUserモデルに認証機能を追加します。

$ rails g devise user
Running via Spring preloader in process 78847
      invoke  active_record
      create    db/migrate/20220825193835_devise_create_users.rb
      create    app/models/user.rb
      invoke    test_unit
      create      test/models/user_test.rb
      create      test/fixtures/users.yml
      insert    app/models/user.rb
       route  devise_for :users

modelファイル、migrateファイルが追加されUserモデル用のルーティングが追加されます。 config/routes.rb には次のような認証機能を使うためのルーティングが追加されます。

# config/routes.rb

Rails.application.routes.draw do
  # 追加されるルーティング
  devise_for :users
  ...

追加されるルーティング一覧です。

$ rails route
                  Prefix Verb      URI Pattern                      Controller#Action
        new_user_session GET       /users/sign_in(.:format)         devise/sessions#new
            user_session POST      /users/sign_in(.:format)         devise/sessions#create
    destroy_user_session DELETE    /users/sign_out(.:format)        devise/sessions#destroy
       new_user_password GET       /users/password/new(.:format)    devise/passwords#new
      edit_user_password GET       /users/password/edit(.:format)   devise/passwords#edit
           user_password PATCH     /users/password(.:format)        devise/passwords#update
                         PUT       /users/password(.:format)        devise/passwords#update
                         POST      /users/password(.:format)        devise/passwords#create
cancel_user_registration GET       /users/cancel(.:format)          devise/registrations#cancel
   new_user_registration GET       /users/sign_up(.:format)         devise/registrations#new
  edit_user_registration GET       /users/edit(.:format)            devise/registrations#edit
       user_registration PATCH     /users(.:format)                 devise/registrations#update
                         PUT       /users(.:format)                 devise/registrations#update
                         DELETE    /users(.:format)                 devise/registrations#destroy
                         POST      /users(.:format)                 devise/registrations#create
...

app/models/user.rb には使用するモジュールが追加されます。 使用するdeviseのモジュールの追加や削除はここで設定するようです。

[*Rails*] deviseの使い方(rails5版) - Qiita

# app/models/user.rb

class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable
end

migrateファイルへのカラム追加等が必要な場合はこのタイミングでファイルを修正します。 今回は全てデフォルトで進めます。 各ファイルの修正が終わったらmigrate します。

$ rails db:migrate

rails s でサーバを起動し追加された以下のルーティングへアクセスするとログイン機能の各ページの表示を確認できます。

  • users/sign_up: サインアップ
  • users/sign_in: ログイン

以上で認証機能付きモデルの追加は完了です。

2. 認証機能用ビューの作成

deviseで作成したページ(ログインページ等)の表示で使用するviewファイルは自分のプロジェクトには存在しません(devise gemから読み込んでいるっぽい)。 このviewファイルを独自にカスタマイズしたい場合は自分のプロジェクト内にdeviseの表示に使うviewファイルを追加する必要があります。

rails g devise:views モデル名複数形 で認証機能付きモデルのviewファイルを追加できます(deviseのデフォルトのviewファイルのコピーが作成される)。 もし、deviseの認証機能で使用するviewファイルにカスタマイズしない場合はこの手順は不要です。

viewファイルの中身の変更は後回しにしてファイル追加と読み込みの設定のみ行います。 今回はUserモデルを指定してrails g devise:views コマンドを実行し、app/views/users/ にdeviseの各モジュール毎のviewファイルを追加します。 ちなみに、モデルを指定しない場合はdeviseを使用する各モデル共通のviewファイルがapp/views/devise に追加されます。

$ rails g devise:views users
Running via Spring preloader in process 21582
      invoke  Devise::Generators::SharedViewsGenerator
      create    app/views/users/shared
      create    app/views/users/shared/_error_messages.html.erb
      create    app/views/users/shared/_links.html.erb
      invoke  form_for
      create    app/views/users/confirmations
      create    app/views/users/confirmations/new.html.erb
      create    app/views/users/passwords
      create    app/views/users/passwords/edit.html.erb
      create    app/views/users/passwords/new.html.erb
      create    app/views/users/registrations
      create    app/views/users/registrations/edit.html.erb
      create    app/views/users/registrations/new.html.erb
      create    app/views/users/sessions
      create    app/views/users/sessions/new.html.erb
      create    app/views/users/unlocks
      create    app/views/users/unlocks/new.html.erb
      invoke  erb
      create    app/views/users/mailer
      create    app/views/users/mailer/confirmation_instructions.html.erb
      create    app/views/users/mailer/email_changed.html.erb
      create    app/views/users/mailer/password_change.html.erb
      create    app/views/users/mailer/reset_password_instructions.html.erb
      create    app/views/users/mailer/unlock_instructions.html.erb

作成したviewファイルを使って表示をするようにconfig/initializers/devise.rb に設定を追加します。 config.scoped_viewstrueに設定します。

# config/initializers/devise.rb

Devise.setup do |config|
  ...
  # ここのコメントアウトを外してtrueにする
  # config.scoped_views = false

  config.scoped_views = true

サーバーを再起動すると追加したviewファイルを参照するようになります。

3. 認証機能用コントローラ作成

viewファイル同様にdeviseで作成したページ(ログインページ等)の表示で使用するcontrollerファイルは自分のプロジェクトには存在しません。

deviseのコントローラをカスタマイズする場合はrails g devise:controller モデル名複数形 で認証機能付きモデルのcontrollerファイルを追加できます(deviseのデフォルトのcontrollerファイルのコピーが作成される)。

contlollerファイルの中身の変更と設定は後回しにてファイル追加のみを行います。

Userモデルを指定してrails g devise:controller コマンドを実行するとapp/controllers/users/ に各モジュール毎のcontrollerファイルが追加されます。

$ rails generate devise:controllers users
Running via Spring preloader in process 39155
      create  app/controllers/users/confirmations_controller.rb
      create  app/controllers/users/passwords_controller.rb
      create  app/controllers/users/registrations_controller.rb
      create  app/controllers/users/sessions_controller.rb
      create  app/controllers/users/unlocks_controller.rb
      create  app/controllers/users/omniauth_callbacks_controller.rb
===============================================================================

Some setup you must do manually if you haven't yet:

  Ensure you have overridden routes for generated controllers in your routes.rb.
  For example:

    Rails.application.routes.draw do
      devise_for :users, controllers: {
        sessions: 'users/sessions'
      }
    end

===============================================================================72681

app/models/user.rb で有効化したモジュール毎にcontrollerファイルが作成されます。

例) デフォルトて有効化されているモジュールの場合のcontrollerファイル一覧

$ ls -1 app/controllers/users/
confirmations_controller.rb
omniauth_callbacks_controller.rb
passwords_controller.rb
registrations_controller.rb
sessions_controller.rb
unlocks_controller.rb

作成したcontrollerファイルを修正することでdeviseの認証機能をカスタマイズできます。

controllerファイルに関してはviewファイルと異なり読み込み設定は不要ですが、controller毎にルーティングの設定が必要になります。 今回は記事ではこちらの設定は行いません。

heartcombo/devise: Flexible authentication solution for Rails with Warden.

4. ユーザ名表示、ログアウトボタン設置

全ページ共通のviewであるapp/views/application.html.erb にユーザ名(デフォルトで用意されているemail カラムの値)とログアウトボタンを設置します。

  • user_signed_in? メソッドはサインインしていればtrue、そうでなければfalse を返す。
  • current_user メソッドでログインユーザの情報が取得する。

Rails deviseで使えるようになるヘルパーメソッド一覧 - Qiita

# app/views/application.html.erb

<!DOCTYPE html>
<html>

...
<body>
  ...
  # 下記を追加 
  <% if user_signed_in? %>
  <p>User: <%= current_user.email %></p>
  <%= link_to 'Logout', destroy_user_session_path, method: :delete %>
  <% end %>

  <%= yield %>
</body>

</html>

if user_signed_in? とすることでログイン時のみユーザ名(email)とログアウトボタンが表示されます。

5. その他の追加設定

ログイン機能に必須ではないですが以下の2つを設定しました。

  • 未ログインでログインページ以外を開けないようにする
  • ログイン後のリダイレクト先を指定する

未ログインでログインページ以外を開けないようにする

未ログインでログインページ以外のページにアクセスするとログインページ(users/sign_in)にリダイレクトするように設定します。

deviseのauthenticate_モデル名単数! メソッドでログイン済みユーザのみにアクセスを許可することができます。

全controllerで読み込まれるapp/controller/application_controller.rb に設定を追加することで全ページへ設定を反映させます。

Rails deviseで使えるようになるヘルパーメソッド一覧 - Qiita

# app/controller/application_controller.rb

...
class ApplicationController < ActionController::Base
  # 下記を追記
  before_action :authenticate_user!
end

ログイン後のリダイレクト先を指定する

deviseを使ったログイン認証後のリダイレクト先はデフォルトではアプリケーションのルート(/)に設定されています。

config/routes.rb のルートのURLをbooks#index に変更することでログイン後のリダイレクト先を指定します。

# config/routes.rb

Rails.application.routes.draw do
  # 下記を追加
  root 'books#index'
  devise_for :users
  resources :books
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end

root の追加を確認します。

$ rails routes
   Prefix Verb        URI Pattern                   Controller#Action
     root GET         /                             books#index
    books GET         /books(.:format)              books#index
          POST        /books(.:format)              books#create
 new_book GET         /books/new(.:format)          books#new
edit_book GET         /books/:id/edit(.:format)     books#edit
     book GET         /books/:id(.:format)          books#show
          PATCH       /books/:id(.:format)          books#update
          PUT         /books/:id(.:format)          books#update
          DELETE      /books/:id(.:format)          books#destroy
...

ログイン後にbooks#index ページにリダイレクトするようになりました。

devise gemを使ったログイン機能の実装は以上です。

まとめ

deviseを初めて触ってviewもcontlollerも見当たらないのにログイン機能が動作している事が気持ち悪かったですが、少しは理解が進んだ気がします。 一部の機能しか使えていないので少しずつ使い方に慣れていこうと思います。

参照

[*Rails*] deviseの使い方(rails5版) - Qiita

Railsにdeviseをインストールしてセットアップする - karlley's tech blog

heartcombo/devise: Flexible authentication solution for Rails with Warden.

Rails deviseで使えるようになるヘルパーメソッド一覧 - Qiita

Rails deviseで使えるようになるヘルパーメソッド一覧 - Qiita