###ログイン状態を保持
ユーザーの暗号化済みIDと記憶トークンをブラウザの永続cookiesに保存して、永続セッションを作成する準備ができました。
これを実際に行うにはcookiesメソッド
を使います。
####authenticated?をUserモデルに追加する
app/models/user.rb
class User < ApplicationRecord
.
.
.
# 渡されたトークンがダイジェストと一致したらtrueを返す
def authenticated?(remember_token)
BCrypt::Password.new(remember_digest).is_password?(remember_token)
# remember_tokenとremamber_digestを比較するもの
# bycryptで暗号化
# .is_password?で==の働きをする
end
end
トークンとダイジェストの違いがわからん。
####ログインしてユーザーを保持する
app/controllers/sessions_controller.rb
class SessionsController < ApplicationController
.
.
.
def create
user = User.find_by(email: params[:session][:email].downcase)
# 送信されたメアドを使ってデータベースから取り出す。
#emailを小文字にする
if user &.authenticate(params[:session][:password])
# user 取得したユーザーが有効かどうか?
# その後にデータベース上にパスワードがあるか?
# ユーザーログイン後にユーザー情報のページにリダイレクトする
# &.は省略型
log_in user
# ユーザーのブラウザ内の一時cookiesに暗号化済みのユーザーIDが自動で作成
remember user
# ダイジェストをデータベースに記憶させる
redirect_to user
# 名前付きルート/userのビューを表示する
else
flash.now[:danger] = 'Invalid email/password combination'
# flash.nowでリクエストが発生後メッセージを消滅する
# エラーメッセージを作成する
render 'new'
# newアクションのビューを表示
end
end
.
.
.
end
####ユーザーを記憶する
app/helpers/sessions_helper.rb
module SessionsHelper
# 渡されたユーザーでログインする
def log_in(user)
session[:user_id] = user.id
#sessionメソッドで作成した一時cookiesは自動的に暗号化される
# ユーザーのブラウザ内の一時cookiesに暗号化済みのユーザーIDが自動で作成
end
# ユーザーのセッションを永続的にする
def remember(user)
user.remember
# userをデータベースに保存
cookies.permanent.signed[:user_id] = user.id
# signed.permanent ユーザーidと記憶トークンを結びつける
# 攻撃者に奪い取られる可能性があるため
cookies.permanent[:remember_token] = user.remember_token
end
.
.
.
end
####永続的セッションのcurrent_userを更新する
app/helpers/sessions_helper.rb
class User < ApplicationRecord
.
.
.
# 記憶トークンcookieに対応するユーザーを返す
def current_user
if (user_id = session[:user_id])
# ログイン中のidを代入する
# 永続セッション(ログイン中)にするため
@current_user ||= User.find_by(id: user_id)
# カレントユーザーまたは検索結果があったものを代入する
elsif (user_id = cookies.signed[:user_id])
# そうでなければ 書名付きクッキーを代入
user = User.find_by(id: user_id)
# idでユーザーを探す
if user && user.authenticated?(cookies[:remember_token])
# ユーザーが有効であり、記憶トークンも認証されたら
log_in user
# ログインする
@current_user = user
# カレントユーザーに代入するかな
end
end
end
.
.
.
end
テスト
ubuntu:~/environment/sample_app (advanced-login) $ rails t
Running via Spring preloader in process 8950
Started with run options --seed 17566
ERROR["test_login_with_valid_information_followed_by_logout", #<Minitest::Reporters::Suite:0x000055a0ef5c93c0 @name="UsersLoginTest">, 1.6133994830006486]
test_login_with_valid_information_followed_by_logout#UsersLoginTest (1.61s)
ActionView::Template::Error: ActionView::Template::Error: undefined local variable or method ` ' for #<#<Class:0x000055a0eef11190>:0x000055a0ef288cb0>
app/helpers/sessions_helper.rb:32:in `current_user'
app/helpers/sessions_helper.rb:43:in `logged_in?'
app/views/layouts/_header.html.erb:8
app/views/layouts/application.html.erb:9
test/integration/users_login_test.rb:43:in `block in <class:UsersLoginTest>'
24/24: [============================] 100% Time: 00:00:01, Time: 00:00:01
Finished in 1.99939s
24 tests, 58 assertions, 0 failures, 1 errors, 0 skips
ActionView::Template::Error: undefined local variable or method
###演習
1.
ブラウザのcookieを調べ、ログイン後のブラウザではremember_tokenと暗号化されたuser_idがあることを確認してみましょう。
確認
2.
コンソールを開き、リスト 9.6のauthenticated?メソッドがうまく動くかどうか確かめてみましょう。
>> user = User.first
(1.6ms) SELECT sqlite_version(*)
User Load (0.8ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]]
=> #<User id: 1, name: "a", email: "abc@def.com", created_at: "2021-10-05 04:11:41", updated_at: "2021-10-09 07:53:18", password_digest: [FILTERED], remember_digest: "$..">
>> user.authenticated?
Traceback (most recent call last):
2: from (irb):2
1: from app/models/user.rb:48:in `authenticated?'
ArgumentError (wrong number of arguments (given 0, expected 1))
#引数を一つ欲しいらしい。
>> user.authenticated?("")
=> false
>> user.authenticated?("aaaaaa")
=> false