login(forgot password) =>
(rooting ->) password_resets controller new action -> new view
パスワード再設定のためのフォーム画面
form_forを使って、メールアドレスを入力してもらい、params[:email]に格納
params[:email]の送信先は、POST /password_resets であり、createアクションに送られる
-> create action
def create
@user = User.find_by(email: params[:password_reset][:email].downcase)
# userのemailからUser情報を取得
if @user # もしそのemailをもつUserが存在したら
@user.create_reset_digest #token発行、tokenのハッシュ化、reset_digestに保存
@user.send_password_reset_email #emailを送信
flash[:info] = "Email sent with password reset instructions"
redirect_to root_url
else # email情報がDBになければ
flash.now[:danger] = "Email address not found"
render 'new' #もう一度フォームに戻ってもらう
end
end
create_reset_digestメソッドは、前章ではbefore_actionでsignup前に作動させていたが、今回はcreateアクションに組み込んでいる、場所は違うが、順序や機能は同じ
def create_reset_digest
self.reset_token = User.new_token
update_attribute(:reset_digest, User.digest(reset_token))
update_attribute(:reset_sent_at, Time.zone.now)
end
send_password_reset_emailメソッドは、新たにpassword_reset()メソッドを実装する必要がある
def send_password_reset_email
UserMailer.password_reset(self).deliver_now
end
def password_reset(user)
@user = user
mail to: user.email, subject: "Password reset" #送信先とサブタイトル
end
メールの文面は
To reset your password click the link below:
<%= edit_password_reset_url(@user.reset_token, email: @user.email) %>
# reset_tokenとemailをUrlに埋め込む、edit_pass-なのでedit actionへのリンク
This link will expire in two hours.
If you did not request your password to be reset, please ignore this email and
your password will stay as it is.
=> Password_resets controllerの edit action
Userに新しいpasswordを入力してもらうフォーム
edit viewに難しい内容はないが、
前章と違い、edit actionだけでなく、update actionまでUserのemail情報を渡す必要がある
Userにもう一度emailを打ち込んでもらうのは面倒なので、
<%= hidden_field_tag :email, @user.email %>
としてUserには見えない形で、update actionまでつなげる
before_action で正しいユーザーだけがedit,update actionへ行けるように
ここで認証を行う
def valid_user
# nilチェックが失敗する、有効化してなかった、認証できなかった場合はTopページへ
unless (@user && @user.activated? &&
@user.authenticated?(:reset, params[:id]))
redirect_to root_url
end
end
=> update action
updateアクションでしたいことは
def update
@user = User.find_by(email: params[:email])
params[:id] == self.reset_token
params[:email] == user.email
@user.password_digest = User.digest(params[:password])
# userが入力したpasswordをハッシュ化して、password_digestに保存
# passwordの再設定が完了
@user.save
end
このようなことだが、再設定に際し、対策することがあるので、書き方は以下のようになる
def update
if params[:user][:password].empty? #passwordが空だったら
@user.errors.add(:password, :blank) #allow_nilがかかる前に強制的にerror
render 'edit'
elsif @user.update_attributes(user_params)# user_paramsでセキュリティ対策 下
log_in @user
flash[:success] = "Password has been reset."
redirect_to @user
else
render 'edit'
end
end
def user_params
params.require(:user).permit(:password, :password_confirmation)
# 渡されるパラメータで適切なものはpass,confirの二つだけ
# digestへ保存されるのは、どうやってしている?
end
パスワードの有効期限が切れていないかチェックをbeforeフィルターで
def check_expiration
if @user.password_reset_expired?
flash[:danger] = "Password reset has expired."
redirect_to new_password_reset_url
end
end
def password_reset_expired?
reset_sent_at < 2.hours.ago
# <をより早いと読むと、2時間よりも早い、つまりまだ2時間経ってないなら
# 有効期限なので、trueが返ってくる
end