かくすけのいろいろ作るブログ

かくすけの開発者ブログです。開発の他いろいろなモノづくりについて書きます。

【Rails5】deviseを使ってログイン機能を作る

こんにちは。かくすけです。
最近家で育てているカイコの卵が一気に孵化を初めて春の訪れを感じるこの頃です。

今回はかくすけWebサイトにログイン機能を追加していきます!
ログインした管理者だけが下の画像でいう③や④の内容を編集できるようにします。

f:id:kakusuke98:20190413183530p:plain

手順

deviseというgemを使います。 このgemはRailsでログインするときに使う定番のgemですね。

参考にしたページ [*Rails*] deviseの使い方(rails5版) - Qiita

Gemfileにdeviseを追加

Gemfile

# ログイン機能実装
gem 'devise'

Gemfileに追加したdeviseをインストールするために「bundle install」を実行します。

$ bundle install

環境で初めてdeviseを使う場合は次のコマンドも実行しましょう。

rails g devise:install
ログイン用モデルの追加

ログイン情報を持つAdminというモデルを作成します。
(Adminの部分はお好みのモデル名にしてOKです。)

$ bundle exec rails g devise admin

以下のように表示され、いくつかのファイルが生成されます。

      invoke  active_record
      create    db/migrate/20190413095444_devise_create_admins.rb
      create    app/models/admin.rb
      invoke    test_unit
      create      test/models/admin_test.rb
      create      test/fixtures/admins.yml
      insert    app/models/admin.rb
       route  devise_for :admins

次のようなマイグレーションファイルが作られます。 ./db/migrate/20190413095444_devise_create_admins.rb f:id:kakusuke98:20190413185945p:plain

deviseの場合メールアドレスでのログインがデフォルトですが 私はメールアドレス以外の文字列でログインできるようにしたいのでちょっとこのファイルを編集します。

# frozen_string_literal: true

class DeviseCreateAdmins < ActiveRecord::Migration[5.2]
  def change
    create_table :admins do |t|
      ## Database authenticatable
      t.string :email,                           default: ""  # ここを変更。メールアドレスが空でも登録できるように。
      t.string :login_name,         null: false, default: ""  # ここを追加
      t.string :encrypted_password, null: false, default: ""

      ## Recoverable
      t.string   :reset_password_token
      t.datetime :reset_password_sent_at

      ## Rememberable
      t.datetime :remember_created_at

      ## Trackable
      # t.integer  :sign_in_count, default: 0, null: false
      # t.datetime :current_sign_in_at
      # t.datetime :last_sign_in_at
      # t.string   :current_sign_in_ip
      # t.string   :last_sign_in_ip

      ## Confirmable
      # t.string   :confirmation_token
      # t.datetime :confirmed_at
      # t.datetime :confirmation_sent_at
      # t.string   :unconfirmed_email # Only if using reconfirmable

      ## Lockable
      # t.integer  :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
      # t.string   :unlock_token # Only if unlock strategy is :email or :both
      # t.datetime :locked_at


      t.timestamps null: false
    end

    add_index :admins, :email,                unique: true
    add_index :admins, :login_name,           unique: true  # ここを追加
    add_index :admins, :reset_password_token, unique: true
    # add_index :admins, :confirmation_token,   unique: true
    # add_index :admins, :unlock_token,         unique: true
  end
end

login_nameを使ってログインさせることにします。
メールアドレスは消してもいいのですが、deviseデフォルトのカラムということで別部分で影響が出そうなので一応残してます。
ただし、"null: false"の設定を消すことでメールアドレスが空でも登録できるようにしました。

変更を保存してdb:migrateします。

$ bundle exec rake db:migrate

マイグレートが実行され、次のように表示されます。

== 20190413095444 DeviseCreateAdmins: migrating ===============================
-- create_table(:admins)
   -> 0.0080s
-- add_index(:admins, :email, {:unique=>true})
   -> 0.0320s
-- add_index(:admins, :login_name, {:unique=>true})
   -> 0.0071s
-- add_index(:admins, :reset_password_token, {:unique=>true})
   -> 0.0078s
== 20190413095444 DeviseCreateAdmins: migrated (0.0550s) ======================
画面の確認

ちゃんとdeviseが正しく動いているか確認してみましょう
railsサーバーを起動します。

$ bundle exec rails s -b 0.0.0.0

ブラウザでログインページにアクセスします。
URLは"http://~~/admins/sign_in"です。
URLは環境に合わせて読みかえてください。
正しく動いていれば、次のようなページが表示されます。

f:id:kakusuke98:20190414190410p:plain

ログインしていないと入れないページを設定する

無事にdeviseを導入することができました!
しかしこのままではログインしていなくても結局すべてのページにアクセスすることができてしまいます。
ログインしていないと入れないページを作りましょう。
ここで、deviseを入れることで使えるようになる便利なヘルパーメソッドがいくつかあります

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

この中でログインしていないと入れないようにするのが"authenticate_user!"です。
私の場合はモデル名を"user"ではなく"admin"にしているので、"authenticate_admin!"ですね。
このヘルパーをcontrollerのbefore_actionに設定します。

私の場合、Serviceモデル関連のページのアクセスを制限したいので、"services_controller.rb"に追記します。

app/controllers/services_controller.rb

class ServicesController < ApplicationController
  before_action :authenticate_admin!  # ここを追加
  before_action :set_service, only: [:edit, :update, :destroy]

  # GET /services
  def index
    @services = Service.all
  end
....

これを保存して"http://~~/services"にアクセスします。
すると、強制的にログインページが表示されます!

アカウントを作ってみる

servicesのページにアクセスできないことが確認できました!
ただ、これってひょっとしたらログインしていてもアクセスできないのでは・・・?
確認用のアカウントを作成して試してみましょう。 ログインページの"Sign up"リンクをクリックします。

f:id:kakusuke98:20190414205901p:plain

するとアカウント作成用のページが表示されます。

f:id:kakusuke98:20190414210036p:plain

メールアドレスとパスワードを適当に入力します。パスワードは同じものを2回入力します。
メールアドレスは実際に使えるものである必要はありません。
(もちろん設定次第ではてきとーなメールアドレスを禁止したりもできますよ!)
"Sign up"ボタンを押すとアカウントが作成され、自動的にログインされます。
ログインしたので"http://~~/services"がちゃんと表示されました!

これで最低限のログイン機能が実装できました! こんなに簡単に実装できちゃうなんて、deviseには感謝ですね!

ちなみにdeviseのデフォルト設定だとロードバランサーがうまく動かなかったり
バージョンによってはaxlsxというExcelファイルを生成するgemと相性が悪かったりするので注意が必要です。

そのあたりはまたそのあたりの対処をするときにまとめますね!

参考:
[*Rails*] deviseの使い方(rails5版) - Qiita
Rails deviseで使えるようになるヘルパーメソッド一覧 - Qiita