0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Railsチュートリアル11章まとめ(アカウントの有効化)

Posted at

新規にユーザーを登録したとき、そのアドレス宛にアカウントを有効化するためのURL付のメールを送信し、そこにアクセスすることでアカウントを有効化する。

AccountActivationsリソース

account_activationsコントローラーの準備

$ rails generate controller AccountActivations

ルートの設定

routes.rb
Rails.application.routes.draw do
  root   'static_pages#home'
  get    '/help',    to: 'static_pages#help'
  get    '/about',   to: 'static_pages#about'
  get    '/contact', to: 'static_pages#contact'
  get    '/signup',  to: 'users#new'
  get    '/login',   to: 'sessions#new'
  post   '/login',   to: 'sessions#create'
  delete '/logout',  to: 'sessions#destroy'
  resources :users
  resources :account_activations, only: [:edit]
end

usersテーブルへカラムを追加。

$ rails generate migration add_activation_to_users activation_digest:string activated:boolean activated_at:datetime

それぞれの役割は、
activation_digest 有効化のためのトークン
activated 有効化済かどうか
ativated_at 有効化された日時

[timestamp]_add_activation_to_users.rb
class AddActivationToUsers < ActiveRecord::Migration[5.0]
  def change
    add_column :users, :activation_digest, :string
    add_column :users, :activated, :boolean, default: false
    add_column :users, :activated_at, :datetime
  end
end

activatedのdefault値をfalseに指定。

$ rails db:migrate
user.rb
class User < ApplicationRecord
  attr_accessor :remember_token, :activation_token
  before_save   :downcase_email
  before_create :create_activation_digest
  validates :name,  presence: true, length: { maximum: 50 }
  .
  .
  .
  private

    # メールアドレスをすべて小文字にする
    def downcase_email
      self.email = email.downcase
    end

    # 有効化トークンとダイジェストを作成および代入する
    def create_activation_digest
      self.activation_token  = User.new_token
      self.activation_digest = User.digest(activation_token)
    end
end

ここで重要なのはcreate_activation_digestメソッド。
このメソッドはbefore_createコールバックによって、createアクションが実行される前に実行される。
このメソッでの処理は、
①activation_token(attribute_accessorで定義した仮の属性)にランダムなトークンを代入。
②activation_digestカラムにトークンをダイジェスト化して代入。
このメソッドでは代入だけで保存する処理がされていないが、コールバックによってcreateアクションの前に実行されるため、アクション内で保存がされる。

そのため、この時点で新しくuserを作れば、activation_digestカラムに値の入ったuserが生成されうようになる。

ちなみに、トークンの生成メソッドとダイジェスト化のメソッドはこれ

user.rb
  def User.digest(string)
    cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
                                                BCrypt::Engine.cost
    BCrypt::Password.create(string, cost: cost)
  end
  
  def User.new_token
    SecureRandom.urlsafe_base64
  end

cookieを使った永続ログインやパスワードの再設定にもこのメソッドを使う。

アカウント有効化のメール送信

次にメーラーの設定。

$ rails generate mailer UserMailer account_activation password_reset

mailers/user_mailer.rbの中にアカウント有効化のためのメソッドとパスワード再設定のためのメソッドが自動で生成される。
しかしそのままでは使えないため編集する。
mailers/application_mailer.rbも生成されているが、こちらは別に触らなくても問題ない。
チュートリアル上では送信元のアドレスを変えているだけなので、そのままでも動作する。

user_mailer.rb
class UserMailer < ApplicationMailer

  def account_activation(user)
    @user = user
    mail to: user.email, subject: "Account activation"
  end

  #パスワード再設定用
  def password_reset
    @greeting = "Hi"

    mail to: "to@example.org"
  end
end

引数でuserを特定し、そのアドレスへメールを送信する。

account_activation.text.erb
Hi <%= @user.name %>,

Welcome to the Sample App! Click on the link below to activate your account:

<%= edit_account_activation_url(@user.activation_token, email: @user.email) %>
account_activation.html.erb
<h1>Sample App</h1>

<p>Hi <%= @user.name %>,</p>

<p>
Welcome to the Sample App! Click on the link below to activate your account:
</p>

<%= link_to "Activate", edit_account_activation_url(@user.activation_token,
                                                    email: @user.email) %>

上二つはメールのテンプレート。
text.erbとなっている方がテキストベース。
ここでアカウントを有効化するためのURLを生成している。
この部分。

edit_account_activation_url(@user.activation_token, email: @user.email) 

例えば、editページへ変移したいときにはedit_user_path(user)と書き、これで/users/:id/editといったURLが生成される。
ここでは、@user.activation_tokenを:idとみなしてURLを生成し、その後にuserのアドレスをくっつけている。つまり、ここでは以下のようなURLを生成する。
/users/トークン/edit/メールアドレス
ここのトークンはparams[:id]で、メールアドレスはparams[:email]でそれぞれ取得できる。

次に、このメールのプレビューを確認する。

user_mailer_preview.rb
# Preview all emails at http://localhost:3000/rails/mailers/user_mailer
class UserMailerPreview < ActionMailer::Preview

  # Preview this email at
  # http://localhost:3000/rails/mailers/user_mailer/account_activation
  def account_activation
    user = User.first
    user.activation_token = User.new_token
    UserMailer.account_activation(user)
  end

  # Preview this email at
  # http://localhost:3000/rails/mailers/user_mailer/password_reset
  def password_reset
    UserMailer.password_reset
  end
end

ここは特に説明の必要はなさそうなのでスキップ。
メソッドの上のURLにアクセスすればプレビューが表示される。開発環境によってlocal:3000(host名)を書きかえる。

users_controller.rb
  def create
    @user = User.new(user_params)
    if @user.save
      UserMailer.account_activation(@user).deliver_now
      flash[:info] = "Please check your email to activate your account."
      redirect_to root_url
    else
      render 'new'
    end
  end

新しいuserを生成するためのアクション。
ここで重要なのは、
UserMailer.account_activation(@user).deliver_now
の一行のみ。
user_mailer.rbで記述したaccount_activation(user)メソッドを呼び出し、引数に@userが入っている。この@userのメールアドレスに向けてメールを送信する、という処理になっている。
これでuserの新規作成は完了。
後はactivatedをdefaultのfalseからtrueに変えてやる処理だけ。

アカウントを有効化する

アカウントを有効化するためには、activation_digestの値とactivation_tokenの値が一致するかを確認する。
ここで、ログイン永続化のため、remember_digestとremember_tokenが一致するか調べるために用いたauthenticater?メソッドを使うが、今のままでは使用できない。

user.rb
def authenticated?(remember_token)
  return false if remember_digest.nil?
  BCrypt::Password.new(remember_digest).is_password?(remember_token)
end

このメソッドはremembet_tokenのためのメソッドなので、このままではactivation_tokenに流用することができない。
これを今回や、パスワード再設定の際にも使用できるように、下のように書き換える。

user.rb
def authenticated?(attribute, token)
  digest = send("#{attribute}_digest")
  return false if digest.nil?
  BCrypt::Password.new(digest).is_password?(token)
end

このメソッドは user.authenticated?(:activation, params[:id])という形で実際にeditアクションの中に組み込む。
引数の:activationはauthenticated?メソッド内でattributeに代入され、activationd_digestとして振る舞うようになる。
第二引数のparams[:id]は、有効化のためのURLに含まれるトークンのこと。
BCrypt::Password.new(digest).is_password?(token)
ここでダイジェストとトークンの比較を行っている。

authenticated?メソッド内で使われているsendメソッドが重要な役割を果たしているので、以下のコンソールでの動作を確認しておく。

>> user = User.first
>> user.activation_digest
=> "$2a$10$4e6TFzEJAVNyjLv8Q5u22ensMt28qEkx0roaZvtRcp6UZKRM6N9Ae"
>> user.send(:activation_digest)
=> "$2a$10$4e6TFzEJAVNyjLv8Q5u22ensMt28qEkx0roaZvtRcp6UZKRM6N9Ae"
>> user.send("activation_digest")
=> "$2a$10$4e6TFzEJAVNyjLv8Q5u22ensMt28qEkx0roaZvtRcp6UZKRM6N9Ae"
>> attribute = :activation
>> user.send("#{attribute}_digest")
=> "$2a$10$4e6TFzEJAVNyjLv8Q5u22ensMt28qEkx0roaZvtRcp6UZKRM6N9Ae"
account_activations_controller.rb
 def edit
    user = User.find_by(email: params[:email])
    if user && !user.activated? && user.authenticated?(:activation, params[:id])
      user.update_attribute(:activated,    true)
      user.update_attribute(:activated_at, Time.zone.now)
      log_in user
      flash[:success] = "Account activated!"
      redirect_to user
    else
      flash[:danger] = "Invalid activation link"
      redirect_to root_url
    end
  end

editアクション。
①URLに含まれているメールアドレスからuserを特定。
②userが存在し、かつ、user.activatedがfalseであり、かつ、activation_digestとURLに含まれているactivation_tokenが一致されば、処理を実行。
③activatedをtrueに更新。
④activated_atを現在時刻(更新された時刻)に更新
⑤ログイン
⑥users/showへ
といった流れ。

最後に、有効化が済んでいないuserのログインを拒否する処理を追加する。

sessions_controller.rb
def create
    user = User.find_by(email: params[:session][:email].downcase)
    if user && user.authenticate(params[:session][:password])
      if user.activated?
        log_in user
        params[:session][:remember_me] == '1' ? remember(user) : forget(user)
        redirect_back_or user
      else
        message  = "Account not activated. "
        message += "Check your email for the activation link."
        flash[:warning] = message
        redirect_to root_url
      end
    else
      flash.now[:danger] = 'Invalid email/password combination'
      render 'new'
    end
  end

if user.activated?
この条件分岐を追加しただけ。trueの時だけログインできる。

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?