自分の手で未来を創るーlav0

自分のために、誰かのために、今ここにないもの、もっと良くしたいもの、何でも自分の手で創っていく。そして、作ったものを公開していきます

Rails 6.0でアプリを開発 基本の流れ⑧ 永続的ログイン機能

今回はremember_me付きのログイン機能を実装するメモ。

f:id:kslabo51:20200304212124j:plain

 

実現したいこと

永続的Cookiesの作成と安全性の高い記憶トークンの生成とトークン認証への活用

 

Cookiesを読みだす方法と対処法

①パケットスニッファで直接Cookiesを取り出す

対処法:SSLの適用

 

②DBから記憶トークンを取り出す

対処法:記憶トークンのハッシュ値を保存

クロスサイトスクリプティングを使う

対処法:Railsが対処

 

④ユーザーがログインしているデバイスを直接操作

 対処法:ユーザーがログアウトするとトークンを必ず変更しデジタル署名も使用

 

 

記憶トークンのハッシュ値を保存

・記憶トークンにはランダムな文字列を使用

トークンをCookiesに保存する際に有効期限を与える

ハッシュ値に変換してトークンをDBに保存

・Cookiesに保存するユーザーIDは暗号化

・IDでDBを検索し、記憶トークンのCookiesがDB内のハッシュ値と一致することを確認

 

1)remember_digest属性をUserモデルに追加

$ rails g migration add_remember_digest_to_users remember_digest:string

$ rails db:migrate

 

2)ユーザーを記憶

記憶トークンを作成し、そのトークンをダイジェストに変換したものをDBに保存

下記①~③のメソッドの作成

 

①new_tokenメソッド

ランダムなトークン生成用メソッド

 

※ユーザーオブジェクトが不要になるのでUserモデルのクラスメソッドとして作成

f:id:kslabo51:20200304200402p:plain

 

②rememberメソッド

トークンの代わりに記憶ダイジェストをDBに保存するメソッド

 

・記憶トークンをユーザーと関連付けトークンに対応する記憶ダイジェストをDBに保存

・バリデーションを素通りさせるためにupdate_attributeを使用して更新

f:id:kslabo51:20200304200656p:plain

※2行目attr_accessor:ユーザーモデルにトークンをDBに保存せずに実装するための仮想の属性を作成するもの

 

 ③authenticated?メソッド

渡されたトークンがダイジェストと一致したらtrueを返すメソッド

 

f:id:kslabo51:20200304200910p:plain

 

 3)次にsessionヘルパー内にも下記メソッドの作成と修正を行う

①rememberメソッド

f:id:kslabo51:20200304201424p:plain

f:id:kslabo51:20200304201442p:plain


 

②createアクション内で呼び出す

f:id:kslabo51:20200304201631p:plain

 

③current_userメソッドの修正

f:id:kslabo51:20200304201753p:plain

 

f:id:kslabo51:20200304201827p:plain

 

 4)ユーザーがログアウトできるように再度設定しなおす

 

①記憶ダイジェストをnilで更新するforgetメソッドの作成

f:id:kslabo51:20200304202027p:plain

 

②sessionヘルパーでもforgetヘルパーメソッドとlog_outメソッドを作成

f:id:kslabo51:20200304202139p:plain

f:id:kslabo51:20200304202238p:plain


 

5)rememberダイジェストが存在しないときはfalseを返す処理を実装

ブラウザで2つのウィンドウでログインし片方でログアウトしてしまったときの処理

 

①テストでチェック(ログアウトをもう一つ追加する)

f:id:kslabo51:20200304203758p:plain

②sessionsControllerのdestroyアクション内にログインしていたらログアウトするという記述を追加

f:id:kslabo51:20200304203948p:plain

 

6)rememberダイジェストがnilの場合falseを返すようにする

2つの別のブラウザでログインした場合にエラーを起こさないように実装

 

①テストでチェック

f:id:kslabo51:20200304205016p:plain

②authenticated?メソッドを修正

f:id:kslabo51:20200304205042p:plain


7)Remember_meチェックボックスでログインを保持する

 

①ログインフォームにチェックボックスを追加

f:id:kslabo51:20200304205244p:plain

※ラベルの内側に配置する

 

CSSの編集

f:id:kslabo51:20200304205327p:plain



8)チェックボックスで記憶するかしないかを判断させる

ON → ユーザーを記憶

OFF → 記憶しない

f:id:kslabo51:20200304205815p:plain



9)テスト内でユーザーがログインするためのヘルパーメソッドを作成

test_helperに2つのlog_in_asメソッドを作成

 

①テストユーザーとしてログインする

②ActionDispatch::IntegrationTest統合テスト用のテストユーザーとしてログインする

f:id:kslabo51:20200304210055p:plain



10)チェックボックスがオン、オフ両方の場合のテスト

f:id:kslabo51:20200304210230p:plain

 

11)テストから仮想の属性(remember_token)にアクセスする方法

f:id:kslabo51:20200304210359p:plain

createアクション内のuserをインスタンス変数に変更(user → @user)

f:id:kslabo51:20200304210421p:plain

アクセスしたいインスタンス変数に対するシンボルをassignの引数で渡す

f:id:kslabo51:20200304210533p:plain

 

12)sessions_helper_test.rbを作成し永続的セッションをテスト

$ touch test/helpers/sessions_helper_test.rb

 

f:id:kslabo51:20200304210739p:plain

 

13)今回はやたらめったらエラーが出まくりました。

エラーの内容は

ActiveRecord::StatementInvalid: SQLite3::BusyException: database is locked

ググるとたくさん出てくるのでよく起きる事象のようですが、サイトで紹介していただいているようにしてもなかなか解除できない。そのくせ、しばらく時間たつと治ってたりもします。

f:id:kslabo51:20200304211537p:plain

原因は複数のtransactionが発生してロックがかかるみたい。この部分もう少し理解せねばほんとの解決にならないかなっと思っています('_')

 

参考にさせていただいたサイト

第9章 発展的なログイン機構 - Railsチュートリアル

ActiveRecord::StatementInvalid: SQLite3::BusyException: database is locked - rochefort's blog