IT技術にまつわる実験ノート

「長編を書くより、短編を数多く完成させてください。それが上達への近道です」 by 手塚治虫

Rails5 で認証機能を作ってみる

はじめに

ここでは、以下の記事を参考にして認証機能を作ってみることにする。

 

動機

devise の README.md に「まずは認証機能を作ってみて」と書いてあったのがキッカケ。

f:id:matt-note:20190306225146p:plain

 

 作ってみる

 ざっくりとアプリを作成する。

  • rails new auth_sample
  • cd auth_sample

 

ルートとなるページを作成する。ジェネレーターを実行後に、config/routes.rb を編集する。

  • bin/rails g controller home index

f:id:matt-note:20190308005447p:plain

 

Gemfile の bcrypt をコメントアウトして、bundle する。

f:id:matt-note:20190308010943p:plain

  • bundle

 

ユーザーを作成する。

  • bin/rails g scaffold User email:uniq password:digest
  • bin/rails db:migrate

 

サーバーを起動して、ユーザーを作成してみる。

f:id:matt-note:20190308012746p:plain

f:id:matt-note:20190308012849p:plain

-> これでユーザー登録(サインイン)機能が作成できた。

 

セッションコントローラを作成して、ログイン・ログアウト機能を作成する。

  • bin/rails g controller sessions new create destroy

 

ルーティングを作成する。フォームの表示には GET リクエスト、フォームの投稿には POST リクエスト、ログアウトには DELETE リクエストで設定する。

f:id:matt-note:20190308130450p:plain

 

SessionsController を作成する。ここでは主に create アクションで、①フォームの値を検証して、その email の値からユーザーを DB から取り出して、②ユーザーのパスワードが正しいか検証して、③正しかったらセッションに user_id を格納して、④リダイレクトする処理を設定する。

f:id:matt-note:20190308131249p:plain

 

ログインで使うフォームを作成する。【参考:ruby on rails - How to replace form_for with form_with for sessions - Stack Overflow

f:id:matt-note:20190308132156p:plain

f:id:matt-note:20190308132413p:plain

-> これでログイン機能が作成できた。

 

ログインしているユーザーを取得する処理は、あちこちで使用するので、専用の current_user メソッドを作成する。ここでは、①ApplicationController でメソッドを定義することで、すべてのコントローラから使えるようにして、②helper_method を使って、すべてのビューから使えるようにする。

この時の session[:user_id] は、1 など具体的な数字になる。これでDBで id でユーザーを検索して、取得したユーザーデータ(ログインした User)を返す処理を作成できた。

f:id:matt-note:20190308133149p:plain

 

app/views/home/index.html.erb を編集する。ここでは主に、①current_user メソッドでユーザーがログインしているかを判断して、②ログインしていたらログアウトのリンクを表示して、③ログインしていなかったら、サインインのリンクとログインのリンクを表示する。

f:id:matt-note:20190308134541p:plain

f:id:matt-note:20190308134612p:plain

f:id:matt-note:20190308134716p:plain

-> これで認証機能を作成できた。

 

その他

authenticate メソッドの説明は以下の通り。authenticate メソッドは引数で渡したパスワードが正しかった場合に、self を返す。それ以外は false を返す。

f:id:matt-note:20190308135604p:plain

f:id:matt-note:20190308135630p:plain

 

password_digest の値は、以下のような hash値になる。

f:id:matt-note:20190308141738p:plain

 

is_password? メソッドの説明は以下の通り。

f:id:matt-note:20190308140101p:plain

 

hash_secret メソッドの説明は以下の通り。hash_secret メソッドは、secret と salt を受け取って、パスワードハッシュを計算する hash値に変換する。

f:id:matt-note:20190308140559p:plain

 

is_password? メソッドの動作は以下のようになる。【参考:Using bcrypt with Sinatra basic auth - yhara.jp

f:id:matt-note:20190308143922p:plain

f:id:matt-note:20190308144127p:plain

f:id:matt-note:20190308144024p:plain

-> is_password? の引数には、"passw0rd" のような文字列(ユーザーからの入力)を渡す。

 

hash_secret メソッドの動作は以下のようになる。"passw0rd"の文字列と salt から、hash値を作成する。

f:id:matt-note:20190308145026p:plain

 

app/model/user.rb で authenticate メソッドをオーバーライドして、値を出力してみる。

f:id:matt-note:20190308145926p:plain

f:id:matt-note:20190308150041p:plain

-> authenticate メソッドは、ユーザーからの入力を受け取って、パスワードが正しいか検証して、正しかったら User モデルを返す処理を実行する。あとは返された User モデルから id を取り出して処理していくことができる。

 

hash値やsalt については以下の記事を参考にする。