TOP > カテゴリ > Ruby・Ruby on Rails >

Deviseでログイン機能をつくる [Ruby on Rails]

今回は日本語化された「Devise」でログイン機能を作ります。

アカウントを仮登録するとシステムからメールが送信されて、そのリンクをクリックしたら本登録という設定にします。また、ログインする際にアカウントの認証に3回失敗したらアカウントをロックするようにします。このロック解除にもメール経由の解除が必要とします。

これによりブルートフォース攻撃(総当たり攻撃)の対策にもなります。

Deviseでは各種操作にメールを経由しない方法もありますが、それだとセキュリティ的に弱いのでメール経由の設定を推奨します。

関連記事:ログイン機能をつくる ※Devise未使用

目次

1. 初期設定
2. Gemfileの追加
3. Deviseのインストール
4. Railsの日本語設定
5. Viewの編集
6. Deviseの設定(モデル/テーブルの生成)
7. Deviseの設定(メール)
8. Deviseの設定(その他)
9. コントローラーフィルター/ヘルパー
10. ストロングパラメータ
11. テキスト形式のメールにする

作るもの - Deviseの機能

[初期画面] ※この記事で作成

[アカウント登録] ※Deviseの機能

[仮登録時にメールが配信される] ※Deviseの機能

テキスト形式だと長いリンクが途切れる場合があるのでHTMLメールです。

[ログイン] ※Deviseの機能

[ログイン後] ※この記事で作成

[ユーザー情報の変更] ※Deviseの機能

[指定認証回数の失敗によるアカウントロック機能] ※Deviseの機能

[メールによるアカウントロックの解除] ※Deviseの機能

前提条件

メール送受信の通信経路をSSL/TLSに対応する (任意)

上記はSSL/TLS対応の独自ドメインのメールサーバー構築です。SMTPSサーバー(465ポート)のユーザー名、パスワードが用意されていてメールが送信出来れば、構築しなくても良いです。

1. 初期設定

1-1. プロジェクトの生成

cd ~/
mkdir myapp
cd myapp
 
rails new . --skip-turbolinks --skip-test -d mysql 

※今回はturbolinks 、testを使用しない。

1-2. データベースの設定

[config/database.yml]

development:
  <<: *default
  database: myapp_development
  username: myapp_development
  password: 1234567890  

1-3. ユーザー/データベースの生成

// MySQL(MariaDB)にログインする
mysql -u root -p
 
// ユーザーの作成
CREATE USER 'ユーザー名' IDENTIFIED BY 'パスワード';
 
// データベースの作成
CREATE DATABASE myapp_development;
 
// ユーザーにデータベース(myapp_development)の権限を付与する
GRANT ALL PRIVILEGES ON myapp_development.* TO 'ユーザー名';

1-4. モデル/コントローラー/ビューの生成

Catモデル、catsコントローラー/ビューを生成します。 ※名称は任意

// モデルの作成(モデル/マイグレーションファイルの生成)
bin/rails g model Cat name:string description:text
 
// マイグレーションの実行(テーブルの生成)
bin/rails db:migrate
※catsテーブルが生成される
 
// コントローラー/ビューの作成(ファイルの生成)
bin/rails g controller cats index show new edit

2. Gemfileの追加

Gemfileに以下を追加してbundleする

# 本体
gem 'devise'
# 以下は日本語化用
gem 'devise-i18n'
gem 'devise-i18n-views'

3. Deviseのインストール

rails g devise:install

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

Some setup you must do manually if you havent 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

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

上記に記載されている4つの手動設定 + αを行います。

3-1. デフォルトURL

[config/environments/development.rb]

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

3-2. rootの設定

[config/routes.rb]

Rails.application.routes.draw do
  get 'cats/index'
  get 'cats/show'
  get 'cats/new'
  get 'cats/edit'
  
  root to: 'cats#index' 
end

3-3. Flashメッセージの設定

[app/views/layouts/application.html.erb]

<!DOCTYPE html>
<html>
  <head>
    <title>Myapp</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag 'application', media: 'all' %>
    <%= javascript_pack_tag 'application' %>
  </head>

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

3-4. Deviseのi18n(国際化)対応のViewを生成

app/views/deviseが生成されます。

rails g devise:i18n:views

3-5. Deviseの日本語の言語ファイルの生成

config/locales/devise.views.ja.ymlが生成されます。

rails g devise:views:locale ja

初期状態のまま実行すると、次のようなエラーが表示されます。

translation missing: ja.activerecord.errors.models.user.attributes.email.blank
translation missing: ja.activerecord.errors.models.user.attributes.email.taken
translation missing: ja.activerecord.errors.models.user.attributes.password.blank

次のコードに置き換えてください。 ※不足分を私が追加しています。

ja:
  activerecord:
    attributes:
      user:
        current_password: "現在のパスワード"
        email: "メールアドレス"
        password: "パスワード"
        password_confirmation: "確認用パスワード"
        remember_me: "ログインを記憶"
    models:
      user: "ユーザ"
    errors:
      models:
        user:
          attributes:
            email:
              blank: "を入力してください。"              
              taken: "は既に使用されています。"
            password:
              blank: "を入力してください。" 
              too_short: "の文字数が短すぎます。" 
            password_confirmation:
              confirmation: "はパスワードと一致しません。"
            current_password:
              blank: "を入力してください。"               
  devise:
    confirmations:
      new:
        resend_confirmation_instructions: "アカウント確認メール再送"
    mailer:
      confirmation_instructions:
        action: "アカウント確認"
        greeting: "ようこそ、%{recipient}さん!"
        instruction: "次のリンクでメールアドレスの確認が完了します。"
      reset_password_instructions:
        action: "パスワード変更"
        greeting: "こんにちは、%{recipient}さん!"
        instruction: "誰かがパスワードの再設定を希望しました。次のリンクでパスワードの再設定が出来ます。"
        instruction_2: "あなたが希望したのではないのなら、このメールは無視してください。"
        instruction_3: "上のリンクにアクセスして新しいパスワードを設定するまで、パスワードは変更されません。"
      unlock_instructions:
        action: "アカウントのロック解除"
        greeting: "こんにちは、%{recipient}さん!"
        instruction: "アカウントのロックを解除するには下のリンクをクリックしてください。"
        message: "ログイン失敗が繰り返されたため、アカウントはロックされています。"
    passwords:
      edit:
        change_my_password: "パスワードを変更する"
        change_your_password: "パスワードを変更"
        confirm_new_password: "確認用新しいパスワード"
        new_password: "新しいパスワード"
      new:
        forgot_your_password: "パスワードを忘れましたか?"
        send_me_reset_password_instructions: "パスワードの再設定方法を送信する"
    registrations:
      edit:
        are_you_sure: "本当に良いですか?"
        cancel_my_account: "アカウント削除"
        currently_waiting_confirmation_for_email: "%{email} の確認待ち"
        leave_blank_if_you_don_t_want_to_change_it: "空欄のままなら変更しません"
        title: "%{resource}編集"
        unhappy: "気に入りません"
        update: "更新"
        we_need_your_current_password_to_confirm_your_changes: "変更を反映するには現在のパスワードを入力してください"
      new:
        sign_up: "アカウント登録"
    sessions:
      new:
        sign_in: "ログイン"
    shared:
      links:
        back: "戻る"
        didn_t_receive_confirmation_instructions: "アカウント確認のメールを受け取っていませんか?"
        didn_t_receive_unlock_instructions: "アカウントの凍結解除方法のメールを受け取っていませんか?"
        forgot_your_password: "パスワードを忘れましたか?"
        sign_in: "ログイン"
        sign_in_with_provider: "%{provider}でログイン"
        sign_up: "アカウント登録"
    unlocks:
      new:
        resend_unlock_instructions: "アカウントの凍結解除方法を再送する"

3-6. Deviseのコントローラーをカスタマイズ

コントローラーをカスタマイズする場合のみです。 ※今回は不要

rails g devise:controllers users

4. Railsの日本語設定

[config/application.rb]

config.time_zone = 'Asia/Tokyo'
config.i18n.default_locale = :ja

5. Viewの編集

[app/views/layouts/application.html.erb]

<!DOCTYPE html>
<html>
  <head>
    <title>Myapp</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag 'application', media: 'all' %>
    <%= javascript_pack_tag 'application' %>
  </head>

  <body>
    <nav>
      <% if user_signed_in? %>
        <strong><%= current_user.email %></strong>
        <%= link_to 'ユーザー情報の変更', edit_user_registration_path %>
        <%= link_to 'ログアウト', destroy_user_session_path, method: :delete %>
      <% else %>
        <%= link_to '新規登録', new_user_registration_path %>
        <%= link_to 'ログイン', new_user_session_path %>
        <% end %>
    </nav>
              
    <p class="notice"><%= notice %></p>
    <p class="alert"><%= alert %></p>
         
    <%= yield %>
  </body>
</html>

6. Deviseの設定(モデル/テーブルの生成)

6-1. Userモデル

Userモデルを生成します。

rails g devise User

[app/models/user.rb]

次のように「confirmable, lockable, timeoutable」を追加します。

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

[deviseのオプション]

オプション内容
database_authenticatableログイン中にユーザーの信頼性を検証するために、パスワードをハッシュしてデータベースに保存します。認証は、POST要求またはHTTP基本認証の両方で実行できます。
registerable登録プロセスを通じてユーザーのサインアップを処理し、ユーザーがアカウントを編集および削除できるようにします。
recoverableユーザーパスワードをリセットし、リセットの指示を送信します。
rememberable保存されたCookieからユーザーを記憶するためのトークンの生成とクリアを管理します。
validatable電子メールとパスワードの検証を提供します。 これはオプションであり、カスタマイズできるため、独自の検証を定義できます。
confirmable ※追加分本登録の確認用のメールを送信する。また、サインイン時にアカウントが既に認証されているかどうかを検証します。
lockable ※追加分指定された回数のサインインに失敗すると、アカウントをロックします。 電子メールを介して、または指定された期間後にロックを解除できます。
timeoutable ※追加分指定された期間内にアクティブでなかったセッションを期限切れにします。
trackable ※追加分サインインカウント、タイムスタンプ、およびIPアドレスを追跡します。
omniauthableOmniAuthサポートを追加します。TwitterやFacebookなどの認証。

※今回はomniauthableは未使用です。

6-2. マイグレーションファイル

[db/migrate/20191130125007_devise_create_users.rb]

全てのコメントを外します。

# 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

6-3. マイグレーションの実行(テーブルの生成)

usersテーブルが生成されます。

bin/rails db:migrate

767バイト制限のエラーが発生する場合はMySQL 5.5.8(MariaDB 10.2.2)以上にするか、データベースの文字コードをUTF8に変更して下さい。

string型は255文字。Rails6以降のデフォルト文字コードはutf8mb4(1文字4バイト換算)です。utf8は1文字3バイト換算です。

7. Deviseの設定(メール)

[config/initializers/devise.rb]

# メールの差出人
config.mailer_sender = 'info@example.com'

[config/environments/development.rb]

# 以下を追記する 
# ※外部メールサーバー利用時はセキュリティ的にSSL/TLSは必須。
# ※25/587ポートだと認証時にユーザー名、パスワードが平文で流れます。
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
config.action_mailer.raise_delivery_errors = true
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
  :address => "smtp.example.com",
  :port => 465,
  :user_name => "user",
  :password => "password",
  :authentication => :plain,
  :ssl => true,
  :tls => true,
  :enable_starttls_auto => true
}  

ローカルのSMTPサーバーを利用する際は設定を変更して下さい。

8. Deviseの設定(その他)

[config/initializers/devise.rb]

# ロック解除にメールを利用する 
config.unlock_strategy = :email

# 3回認証に失敗するとアカウントがロックされる
config.maximum_attempts = 3

# 30分でユーザーセッションをタイムアウト
config.timeout_in = 30.minutes

Deviseの詳細設定は

config/initializers/devise.rb
※例えば、パスワードの長さはconfig.password_lengthで設定可能。
https://github.com/plataformatec/devise (Devise公式)

をご確認ください。 (英文)

アクセス

これで設定は終わりましたので、次のURLへアクセスします。
http://localhost:3000/

これ以後は参考程度です。

9. コントローラーフィルター/ヘルパー

before_action :authenticate_user!
user_signed_in?
current_user
user_session

controller-filters-and-helpers (Devise公式)

10. ストロングパラメータ

Strong Parameters (Devise公式)

11. テキスト形式のメールにする

DeviseはHTMLメールをデフォルトで使用します。テキスト形式のメールに変更する設定はないようです。※ソースかコントローラーを弄れば可能?

メーラー(メールソフト)によってはHTMLの一部をテキストで表示する機能があります。秀丸メールだと<a>タグのリンクは表示されないので、次のようにするとテキストでもリンクURLが表示されます。

// 本登録の確認メール本文
[app/views/devise/mailer/confirmation_instructions.html.erb]

// 元のコード
<p><%= t('.greeting', recipient: @email) %></p>

<p><%= t('.instruction') %></p>

<p><%= link_to t('.action'), confirmation_url(@resource, confirmation_token: @token) %></p>

// 修正後のコード
<p><%= t('.greeting', recipient: @email) %></p>

<p><%= t('.instruction') %></p>

<p><%= link_to t('.action'), confirmation_url(@resource, confirmation_token: @token) %></p>

<p><%= confirmation_url(@resource, confirmation_token: @token) %></p>

[変更前]

[変更後]

コレをやるとHTML側の本文が不自然になるので各自で調整して下さい。

参考文献

https://github.com/plataformatec/devise (Devise公式)
Action Mailer の基礎
Mailer Models
deviseの使い方





関連記事



公開日:2019年12月02日
記事NO:02808