Just do IT

思うは招く

deviseを使ってRailsアプリに簡単なログイン認証を実装してみる

やること

  • devise gemを使ってRailsアプリに簡単なログイン認証を実装してみる

開発環境

deviseを使う準備

rails newRailsアプリの雛形を作成する。 データベースを指定していないので、デフォルトではsqlite3が使われる。

rails new omniauth-test

(今後、omniauthというgemを使ったSNS認証機能も実装する予定のため、ここではomniauth-testという名前にしている)

アプリが作成されたら移動。

cd omniauth-test

deviseをインストールするため、Gemfileの一番下に以下を追記。

gem 'devise'

bundle installする。(bundleと書いてもbunlde installが実行される)

bundle

deviseをGemfileに記述してインストールしたものの、まだこれでは使えない。 以下のコマンドで、deviseの基本機能をRailsアプリにインストールする。

bin/rails g devise:install

するとこんな画面が現れる。

Running via Spring preloader in process 99124
      create  config/initializers/devise.rb
      create  config/locales/devise.en.yml
===============================================================================

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

  1. Ensure you have defined default url options in your environments files. Here
     is an example of default_url_options appropriate for a development environment
     in config/environments/development.rb:

       config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }

     In production, :host should be set to the actual host of your application.

  2. Ensure you have defined root_url to *something* in your config/routes.rb.
     For example:

       root to: "home#index"

  3. Ensure you have flash messages in app/views/layouts/application.html.erb.
     For example:

       <p class="notice"><%= notice %></p>
       <p class="alert"><%= alert %></p>

  4. You can copy Devise views (for customization) to your app by running:

       rails g devise:views

===============================================================================

上からみていくと、まずdeviseの設定ファイルとロケールファイルが作成されている。

create  config/initializers/devise.rb
create  config/locales/devise.en.yml

それ以下には、「初期設定はこうしてよね」と書いてるので、とりあえず言われるがままにやる。

config/environments/development.rb

# 以下を追記
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }

config/routes.rb

# 追記
root to: 'home#index'

※後でコントローラーとメソッドを実装します。

app/views/layouts/application.html.erb

<body>
  # 追記
  <p class="notice"><%= notice %></p> 
  <p class="alert"><%= alert %></p> 
  <%= yield %>
</body>

前準備が終わったので、一旦gitでコミットしておく。

git add . 
git commit -m "deviseを使う前準備完了"

viewファイルの作成

deviseが提供するコマンドで、ユーザー登録に関するviewファイルたちが生成される。ユーザー登録画面や、「パスワードを忘れましたか?」的な画面など、いろんなWEBアプリでよく見かけるやつ。

bin/rails g devise:views

実は、このコマンドを実行しなくても、deviseでログイン認証画面は表示される。 しかし、実際は登録画面をカスタマイズする場合がほとんどなので、上記コマンドを実行し、いろいろいじる。

Userモデルを作成する

ログインしてきたユーザーを保存するため、deviseの機能を持ったUserモデルを作成する。

bin/rails g devise User

通常のモデルを作成する場合は、rails g model Userなどとするが、deviseの機能を受け継ぎたいので、deviseコマンドを入れている。

実行後、こんな画面が表示される。

      invoke  active_record
      create    db/migrate/20200222031348_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

作成されたapp/models/user.rbを見てみると、deviseの機能(モジュール)が記述されているのがわかる。これは、deviseのコマンドでUserモデルを作成したため。

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

デフォルトで有効になっているのは、次のモジュールたち。

モジュール 機能
database_authenticatable ユーザー情報を保存するのにDBを使う
registerable ユーザーを登録、編集、削除できる
recoverable パスワードをリセットできる
rememberable Cookieにログイン情報を保存する
validatable メールアドレスとパスワードをバリデーションする

コメントアウトされているこちらの部分は、デフォルトでは無効になっている。

# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
モジュール 機能
confirmable 登録されたemailに送信する
lockable ログインを数回したらロックする
timeoutable 一定期間でタイムアウトさせる
trackable ログイン時の情報を保存
omniauthable OmniAuth gemと連携する

たとえば、Facebook認証を実装したい場合、omniauthable を有効にする必要がある。

テーブルに保存される内容をチェック

マイグレーションファイルを見て、どんなカラムが作成されるか見てみる。

# frozen_string_literal: true

class DeviseCreateUsers < ActiveRecord::Migration[6.0]
  def change
    create_table :users do |t|
      ## Database authenticatable
      t.string :email,              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 :users, :email,                unique: true
    add_index :users, :reset_password_token, unique: true
    # add_index :users, :confirmation_token,   unique: true
    # add_index :users, :unlock_token,         unique: true
  end
end

emailアドレスや、暗号化されたパスワードが保存されている。

      ## Database authenticatable
      t.string :email,              null: false, default: ""
      t.string :encrypted_password, null: false, default: ""

マイグレーションを実行

マイグレーションを実行する。

bin/rails db:migrate

データベースがsqlite3の場合、rails newをした時点でデータベースは作成されているので、マイグレーションだけでいい。

データベースとしてPostgresqlMysqlなどを使用しているなら、db:createでデータベースを作成する。

bin/rails db:create

現時点でのルーティングを確認

routes.rbを見てみる。

Rails.application.routes.draw do
  devise_for :users
  root to: "home#index"
end

devise_forはこれまたdeviseが勝手に書いてくれてるもの。具体的にはbin/rails g devise Userをしたときに追記されている。

ここでは、ユーザー登録や編集に関係するページへのルーティングを自動で定義してくれている。

bin/rails routes

でルーティングを確認すると

                               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

Devise::SesstionsControllerという専用のコントローラーは作成していないが、devise/sessions#newなどと記述されている。 これは、deviseが定義しているもの。

一旦コミットする

Userモデルを作ったので、ここで一旦コミットしておく。

git add .
git commit -m "モデル作成"

コントローラーを作成

いよいよ、ページを表示させるためコントローラーとビューファイルを作成する。

bin/rails g controller home index

homeコントローラーを作成し、indexアクションを定義している。 これは、ルーティングで設定したとおりにコントローラーとアクションを作っているだけ。

# routes.rb
root to: "home#index"

「ルートパス(localhost:3000)にアクセスしたら、homeコントローラーのindexアクションが実行される」という意味。

作成されたapp/controllers/home_controller.rbを次のように修正する。

class HomeController < ApplicationController
  #追記
  before_action :authenticate_user!, only: :index 

  def index
  end
end

authenticate_user!はdeviseが提供するメソッド。ログインしていない場合にアクセスしたら、ログイン画面にリダイレクトされる。「ログインしてからまた来てねー」みたいな感じ。

app/views/home/index.html.erbに追記

<h1>Home#index</h1>
<p>Find me in app/views/home/index.html.erb</p>

#追記
<%= current_user.email %> 

current_userは、deivseを入れると使えるようになるメソッド。

current_user.emailとすることで、ログインしているユーザーの、DBに保存された情報が参照できる。

参照するときには、カラム名を記述すればいい。current_user.nameなど。

ビュー側だけではなく、コントローラーなどでも使用できる。

ちなみに、作成したモデル名がMemberモデルだったら、current_memberとなる。

ここでは、ログインできたことがわかりやすくなるように、ログイン後は自分のemailを表示させるようにしている。

app/views/layouts/application.html.erbを修正

共通のレイアウトに追記。

  <body>
    # ここから
    <header>
      <nav>
        <% if user_signed_in? %>
          <%= link_to "logout", destroy_user_session_path, method: :delete %>
        <% else %>
          <%= link_to "sign up", new_user_registration_path %>
          <%= link_to "login", new_user_session_path %>
        <% end %>
      </nav>
    </header>
    # ここまで
    <p class="notice"><%= notice %></p>
    <p class="alert"><%= alert %></p>
    <%= yield %>
  </body>

if user_signed_in?はdeviseが提供しているメソッドで、「あなた、ログインしてんの?」とif文で判断している。 ログインしていればtrueを返すため、if~elseまでのコードを実行する。

つまり、ログインしていると、logoutへのリンクが表示される。

ログインしていなければ、signupとloginへのリンクが表示される。

サーバーを起動して画面を確認

サーバーを起動すると

bin/rails s

こんな画面が表示される。

f:id:K_Koh:20200222125335j:plain

sign_up画面へいき、「example@example.com」で登録すると、

f:id:K_Koh:20200222125511j:plain

ビューファイルで記述したcurrent_user.emailが表示されているのがわかる。 ちなみに、登録したパスワードはdeviseが自動で暗号化してくれるので安心。

簡易的だが、これでdeviseを使ったログイン認証が実装できた。

DBをのぞいてみる

サインアップ時に入力したデータが、ちゃんとUsersテーブルに保存されているかチェックしてみる。 コンソールを開く。

rails c

以下コマンドを入力。

User.first

User.firstで、Userモデルの先頭のユーザーを参照できる。

=> #<User id: 1, email: "example@example.com", created_at: "2020-02-22 03:54:34", updated_at: "2020-02-22 03:54:34">
irb(main):002:0> 

登録に使ったメールアドレス、example@example.comがしっかり保存されている。

リポジトリ

今回書いたコードはこちらです。

https://github.com/kotakanazawa/omniauth-test

次はFacebook認証を実装する

続き

k-koh.hatenablog.com

参考