チワワかわいいブログ

RUNTEQでrails勉強する日々の記録

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に含まれているのでわざわざ渡す必要はない