2020/12/18 railsチュートリアル12章
passwordresetsリソース
メール認証の仕組み
トークンを作る
↓
ハッシュ化する
↓
DBに保存
↓
メール文面でトークンを保存
↓
メールを送ったらトークンは忘れる
↓
受信メールのなかでトークンとメールアドレスを認証
流れ
再設定フォームからemailを入力
↓
emailをキーにDBからユーザーを探す
↓
見つかればトークンとトークンからハッシュ値を生成
↓
ハッシュ値はDBに保存、トークンはURLに含めてメール送信
↓
リンクリックでemailとトークン(authenticated?)で比較・認証
↓
認証に成功したら再設定フォームを表示
作成するカラム
reset_digest
reset_sent_at =>最後に送ったやつだけ有効にしたり、有効期限を決めたりするのに使う
rails generate controller PasswordResets new edit --no-test-framework
rails generateに --no-test-frameworkのようにオプションを渡せる。(テストを作らない)
リセット用メールを送る
<%= link_to "(forgot password)", new_password_reset_path %>
sessions/new.html.erb(ログインページ)にパスワードリセット用にリンクを作成
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
トークン作って、reset_digestとreset_sent_atを作成
def send_password_reset_email UserMailer.password_reset(self).deliver_now end
メールを送る
ここのselfはレシーバー、このメソッドを呼び出しているオブジェクトを指す
<%= edit_password_reset_url(@user.reset_token, email: @user.email) %>
テンプレートにまた埋め込みruby
この@userに至るまで
リンクのURLからemailによってユーザーを特定@password_reset_controller
@userとしてユーザーを代入 @password_reset_controller
@user.send_password_reset_emailで@userをレシーバーとしてメソッド呼び出し @usersモデル
@user.UserMailer.password_reset(self).deliver_now として、@userが(Usermailerの)password_reset(@user)メソッドを呼び出し、メソッドチェーンdeliver_nowでメール送信
結果password_reset_contorollerの変数@userと同じ値がpassword_reset(user)メソッドの@userに代入されている
@userはmailerにあるのでメールテンプレで使える
再設定
ユーザー認証
beforeフィルターの新しい使い方
何かをする直前に呼び出すアクションを指定する
editアクションの前に認証用のメソッドを呼び出しておけば、editアクションでは行う必要がない。
before_action :get_user, only: [:edit, :update] #ユーザーの取得 before_action :valid_user, only: [:edit, :update] #ユーザーの認証 def get_user #ユーザーの取得 @user = User.find_by(email: params[:email]) #この@userは他のメソッドでも使うからインスタンス変数 end # 正しいユーザーかどうか確認する def valid_user unless (@user && @user.activated? && @user.authenticated?(:reset, params[:id])) #get_userの@userを使う→bofore_actionのおかげで使えるようになってる #ここのparams[:id]は前回同様URLの中のトークン redirect_to root_url end end
editアクションでユーザーの認証が完了したら再設定用フォームが表示され、updateアクションにフォームの値が渡せることになるが、この時にparams[:email]の情報を保存しておく必要がある。
また、(悪意を持って)PATCHリクエストを送られることもありうるため、updateアクションの前にも認証を行っておく必要がある。
パスワード更新
条件
・有効期限
def check_expiration @password_reset_controller if @user.password_reset_expired? flash[:danger] = "Password reset has expired." redirect_to new_password_reset_url end end # パスワード再設定の期限が切れている場合はtrueを返す def password_reset_expired? @user.rb <span style="color: #ff0000">(self.)</span>reset_sent_at < 2.hours.ago #reset_sent_atのカラムの値 end #passwordcontrollerの中で、usersオブジェクトに対して使うメソッドであるためuser.rbにメソッドを追加 3.hours.ago < 2.hours.ago #と書き換える => true #3時間前は有効期限切れ
・から文字ではないか
Userモデルのvalidationと干渉しないようにアクションの中でエラーを発生させるようにする
def update if params[:user][:password].empty? #params[:password]がブランクかどうか? @user.errors.add(:password, :blank) #エラーメッセージの追加 render 'edit'
・無効なパスワードではないか
elsif @user.update(user_params) #strongparameterで弾く log_in @user flash[:success] = "Password has been reset." redirect_to @user else render 'edit' #モデルvalidationに引っ掛かったらここに到達 end
editのテンプレート
<%= hidden_field_tag :email, @user.email %>
before_actionでget_userがあるが、updateアクションでもparams[:email]の値が使える必要がある。
テンプレートの中でhidden_field_tag を使うことで、paramsにデータが渡せる。
ここの@userは、editアクションで、認証クリックのemailからとってきたユーザー
トークンはURLに含まれているのでわざわざ渡す必要はない