Ruby on Rails

【Ruby on Rails】deviseを使ってユーザの認証機能をカンタン実装

Railsで一番有名なユーザ認証機能のGemがdeviseです。

Ruby on Rails チュートリアルではユーザの認証機能をスクラッチで開発しますが、deviseを使うとカンタンにユーザの認証機能を実装することができます。deviseを使ってユーザ認証機能を実装したときのメモです。

前提条件

rails 5.0.0.1
devise 4.2.0

deviseのインストール

1. プロジェクトの作成

$ rails _5.0.0.1_ new devise_app
$ cd devise_app

詳細はRailsでWEBアプリケーションを作成するときの備忘録より

2. Gemfileを編集してインストール

Gemfileにdeviseを追加

source 'https://rubygems.org'

...

gem 'devise'
...

$ bundle install

deviseの初期設定

generateコマンドでdeviseをインストール
以下のようなメッセージが表示されるので、それどおりに設定していく

$ rails g devise:install
Running via Spring preloader in process 50039
      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

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

1. デフォルトURLの指定

config/environments/development.rbに以下を記載

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

2. root_urlの指定

1番で指定したURLにアクセスした際に表示されるページを指定する
まだ、ページがないのでgenerateコマンドで作成する

$ rails g controller Home index

config/routes.rbを以下のように修正

Rails.application.routes.draw do
  root 'home#index'
end

3. flashメッセージの設定

app/views/layouts/application.html.erb

<!DOCTYPE html>
<html>
  <head>
    <title>DeviseApp</title>
    <%= csrf_meta_tags %>

    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>

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

    <%= yield %>
  </body>
</html>

4. deviseのViewを生成

$ rails g devise:views

たくさんファイルが生成される

Userモデルの設定

登録情報を保存するためのモデルを作成していく

1. Userモデルを生成

モデルのgenerateコマンドではなく、deviseのコマンドで生成する

$ rails g devise User

db/migrate/タイムスタンプ_devise_create_users.rbに、以下のようなマイグレーションファイルが生成される

class DeviseCreateUsers < ActiveRecord::Migration[5.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
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable
end

デフォルトでは、

  • Database authenticatable: パスワードを暗号化してDBに保存。認証はPOSTリクエスト or HTTP Basic認証
  • Registerable: ユーザ自身がサインアップ・編集・削除することを許可
  • Recoverable: パスワードをリセットして、それを通知
  • Rememberable: Cookieでユーザのを記憶するトークンを生成・削除
  • Trackable: サインイン回数・時間、IPアドレスを保存
  • Validatable: Emailやパスワードのバリデーション

が、ONになっている

  • Confirmable: メールで配信して、URLクリックしたら本登録
  • Lockable: 一定回数サインインに失敗するとアカウントロック
  • Timeoutable: 一定時間活動してないアカウントのセッションを破棄
  • Omniauthable: TwitterやFacebookの認証を実装するために使用

をONにするには、以下の修正が必要

まず、UserModelを変更する
今回は、omniauthable以外全部実装してみる

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

次に、マイグレーションファイルを変更する

class DeviseCreateUsers < ActiveRecord::Migration[5.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

最後に、マイグレーションコマンドを叩く

$ rails db:migrate

Viewの編集

app/views/layouts/application.html.erb

<!DOCTYPE html>
<html>
  <head>
    <title>DeviseApp</title>
    <%= csrf_meta_tags %>

    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>

  <body>
    <header>
      <nav>
        <ul>
        <% if user_signed_in? %>
          <li>
            <%= link_to 'Edit', edit_user_registration_path %>
          </li>
          <li>
            <%= link_to 'Logout', destroy_user_session_path, method: :delete %>
          </li>
        <% else %>
          <li>
            <%= link_to 'Sign-up', new_user_registration_path %>
          </li>
          <li>
            <%= link_to 'Login', new_user_session_path %>
          </li>
        <% end %>
        </ul>
      </nav>
    </header>
    <p class="notice"><%= notice %></p>
    <p class="alert"><%= alert %></p>
    <%= yield %>
  </body>
</html>

Confirmable - アカウント登録確認メールを送信

1. confirmableとは?

新規登録入力後確認メールを送信して、届いたメールのURLがクリックされるとログイン可能になる仕組み

2. メールの設定

config/initializers/devise.rbに、送信に使用するメールアドレスを設定する

Devise.setup do |config|

  ...

  config.mailer_sender = '使用するメールアドレス'

  ...

end

config/environments/development.rbにメールのsftp情報を入力する

  ...

  # mail setting
  config.action_mailer.delivery_method = :smtp
  config.action_mailer.smtp_settings = {
    :address => "smtpサーバー名",
    :port => '587',
    :user_name => "アカウント名",
    :password => "パスワード",
    :authentication => :login,
    :enable_starttls_auto => true
  }
end

ここで注意!
もし設定を間違えていて、535 5.7.0 authentication failed.というエラーが出たら、ソース修正後、Railsのサーバーを必ず再起動する

どうやら、再起動しないと設定ファイルが読み込まれない

3. 届いたメールを確認する

メールを正しく設定できると、サインアップ後に登録したメールアドレス宛に有効化リンクが付いたメールが届く
リンクをクリックすると、ユーザが有効化されてログインできるようになる

app/views/devise/confirmation_instructions.html.erbにてメールの内容を変更できる

Lockable - アカウントをロック

Locableとは?

アカウント認証を一定回数以上間違えると、アカウントロックする機能
うーん、ブルートフォースとか防げるっちゃ防げるけど、
メールリストにヒットしたユーザのアカウントを全部ロックされちゃ困るわな
単純な、嫌がらせは防げるのかな?
ひとまず、実装してみまっしょ

設定方法

config/initializers/devise.rbで以下のような記載をする
今回は、5回ログインに失敗したら、登録したメールアドレスにアンロックのURLを送信するような設定を
maximum_attemptsの数字は、失敗してもいい回数なので4に設定(つまり5回目でNG)

  ...

  config.unlock_strategy = :email
  config.maximum_attempts = 4

  ...

Timeoutable - 一定時間アクセスのないセッションを破棄

Timeoutableとは?

一定時間アクセスがないセッションをタイムアウトにする機能

設定方法

config/initializers/devise.rbにて設定
config.timeout_inに〇〇分後にタイムアウトする時間を設定

  ...
  config.timeout_in = 30.minutes
  ...

アクセス制限をかける

ここまででだいたいサインアップ・サインインの機能を実装した
しかし、全ページにアクセスできてはせっかくの認証の意味がない
そこで、Home#indexにはログインしたユーザしかアクセスできないようにする
ログインしていないユーザがHome#indexつまりrootの画面にアクセスした場合、ログイン画面に遷移するようにせっていする

ログインしていないユーザはログイン画面に強制リダイレクト

app/controllers/application_controller.rbbefore_action :authenticate_user!を追記

各コントローラー毎にも設定できる

まとめ

Railsチュートリアルで結構たいへんなアカウント認証機能づくりですが、deviseを使うとカンタンに実装できました

deviseを利用するとソーシャルログイン機能も実装できる (TODO いつか実装してみる)

Devise + CanCanCan + rolifyを組み合わせることで、ユーザの権限も管理できる (TODO いつか実装してみる)

-Ruby on Rails