パスワードのリセット機能①
何が何だか分からなくなってきたので、小分けにしてまとめ直します。
まず、sorceryのreset_passwordサブモジュールを使ったパスワードリセット機能から。
reset_passwordサブモジュールをインストール
bundle exec rails g sorcery:install reset_password --only-submodules
このコマンドにより、以下のマイグレーションファイルが生成される。
class SorceryResetPassword < ActiveRecord::Migration def change add_column :users, :reset_password_token, :string, default: nil add_column :users, :reset_password_token_expires_at, :datetime, default: nil add_column :users, :reset_password_email_sent_at, :datetime, default: nil # wikiに書いていない以下二つも自動的に作成されていた add_column :users, :access_count_to_reset_password_page, :integer, default: 0 add_index :users, :reset_password_token end end
※ reset_password_tokenにunique制約を付与する。
add_index :users, reset_password_token, unique: true
反映させておく。
user.rbにバリデーションを追加
いつも通り、DB側にunique: trueを追加したので、バリデーションにもuniqueness: trueを追加する。加えて、allow_nil: true
も追加する。
トークンはデフォルトがnilで設定されているので、nilを許容してあげないとデフォルトのnilが重複と見なされて複数のユーザーが登録できなくなってしまう。
validates :reset_password_token, uniqueness: true, allow_nil: true
メイラーの作成
bundle exec rails g mailer UserMailer reset_password_email
コントローラーと似ている(違う点もあるらしい)
上のコマンドにより、生成されたuser_mailer.rb内にreset_password_emailメソッドが記載されている。
def reset_password_email end
↓
# 引数にuserを追加する def reset_password_email(user) @user = User.find(user.id) @url = edit_password_reset_url(@user.reset_password_token) mail(to: user.email, subject: "Your password has been reset") end
↓ user_mailer.rbで取得した変数をメールテキストで用いる
# app/views/user_mailer/reset_password_email.text.erb Hello, <%= @user.email %> =============================================== You have requested to reset your password. To choose a new password, just follow this link: <%= @url %>. # このリンクで後に作成するコントローラのeditアクションに飛ばされる Have a great day!
パスワードリセットに関するコントローラーを作成する
bundle exec rails g controller PasswordResets new edit
# app/controllers/password_resets_controller.rb class PasswordResetController < ApplicationController # before_action:require_loginが宣言されていない場合、エラーが発生する skip_before_action :require_login def new; end # パスワードのリセット要求 def create # ユーザーがパスワードのリセットフォームに入力したメアドがここに送られる。 @user = User.find_by_email(params[:email]) # @userがtrueならば、リセット方法が書かれたメールをユーザーに送信する @user.deliver_reset_password_instructions! if @user redirect_to(root_path, notice: ‘Instructions have been sent to your email.’) # メール自体が送信成功していてもいなくてもメールが送信された内容のフラッシュメッセージを出す。システムに存在する電子メールに関する情報を、悪意あるユーザーに漏らさないため。 end # パスワードのリセットフォーム # メールに記載されたトークンを含むURLからトークンを取得 def edit @token = params[:id] @user = User.load_from_reset_password_token(params[:id]) if @user.blank? not_authenticated return end end # ユーザーがパスワードのリセットフォームを送信した時に発生する def update @token = params[:id] @user = User.load_from_reset_password_token(params[:id]) if @user.blank? not_authenticated return end @user.password_confirmation = params[:user][:password_confirmation] if @user.change_password(params[:user][:password]) redirect_to(root_path, notice: ‘Password was successfully updated.’) else render :edit end end end
resources :password_resets, only: %i[new create edit update]
new.html.erbにパスワード変更メールを送るメールアドレス入力フォームを作成する
<!-- createアクションへ送信 --> <%= form_with url: password_resets_path, local: true do |f| %> <div class="form-group"> <%= f.label :email, User.human_attribute_name(:email) %> <%= f.email_field :email, class: 'form-control' %> </div> <div class="text-center"> <%= f.submit (t'.submit'), class: 'btn btn-primary' %> </div> <% end %>
このフォームにメールアドレスを入力すると、password_resetsコントローラのcreateアクションが起動し、送られたメアドからユーザーを検索、ユーザーが存在するならばそのユーザーにメールを送信する。トップページに遷移し、メールが送られたかどうかに関わらずフラッシュメッセージが表示される。(実在しないメアドだったときに失敗メッセージを表示してしまう仕様だと、もし適当にメールアドレスを入力してもそれが実在するかどうか他者が判別できてしまうため。)
def create @user = User.find_by_email(params[:email]) @user.deliver_reset_password_instructions! if @user redirect_to(root_path, notice: ‘Instructions have been sent to your email.’) end
次回、letter_opener_webを用いた擬似メールプレビュー
続く>>>