ログイン機能を作成する(パート4:ログイン機能実装する)
ここからは念願のログイン機能を実装していきます。ログイン機能はログインするためのフォーム画面を表示し、送られてきた情報を元にユーザーを認証させてあげます。またログアウトもできるようにします。
ログインフォームを作成する
ここからはいつも通りの流れです。まずはログイン機能に必要なコントローラーを作成します。Railsに置ける”ログインする”というのは「セッションというリソースを作る」ことに当たります。そのため、SessionsControllerという名前でコントローラーを作成します。
bundler exec rails g controller Sessions new
ルーティングが自動で定義された状態だと'sessions/new'
になっており適切ではないので、修正していきます。(config/routes.rb)
Rails.application.routes.draw do
get '/login', to: 'sessions#new'
...
end
これでログインフォームを表示させるアクションのURLが/loginになりました。次にログインフォームを表示させるためのviewを作成します。(app/views/sessions/new.html.erb)
<div>
<h1>ログイン</h1>
<%= form_with scope: :session, local: true do |f| %>
<div class="form-group">
<%= f.label :email, 'メールアドレス' %>
<%= f.text_field :email, class: 'form-control',
id: 'session_password' %>
</div>
<div class="form-group">
<%= f.label :password, 'パスワード' %>
<%= f.password_field :password, class: 'form-control',
id: 'session_password' %>
</div>
<div>
<%= f.submit ' ログインする', class: 'btn btn-primary' %>
</div>
<% end %>
</div>
これでログインのフォーム画面が完成です。
ログインを実行する
ここからはログイン画面からメールアドレスとパスワードを入力してログインを実行する部分を作成します。
まずはルーティングの設定を追加します。
Rails.application.routes.draw do
get '/login', to: 'sessions#new'
get '/login', to: 'sessions#create'
...
end
ログインを実行するためのcreateアクションをSessionsController
に追加します。(app/controllers/sessions_controller.rb)
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by(email: session_params[:email])
if user&.authenticate(session_params[:password])
session[:user_id] = user_id
redirect_to root_path, notice: 'ログインしました。'
else
render :new
end
end
private
def session_params
params.require(:session).permit(:email, password)
end
end
まず、user = User.find_by(email: session_params[:email])
で送られてきたメールアドレスでユーザーを検索します。メールアドレスが存在する場合は送られてきたパスワードによる認証をauthenticateメソッドで行います。authenticateメソッドはUserクラスにhassecurepasswordを記述したことで自動で追加された認証メソッドです。引数で受け取ったパスワード((session_params[:password])
)をハッシュ化し、その結果がUserオブジェクト内部に保存されるdigestと一致するか調べます。一致していたら認証成功でUserオブジェクト自身を返し、一致しなければ認証失敗でfalseを返します。
ログイン情報の取得を簡単にする
ユーザーがログインしていればsession[:user_id]にユーザーのIDが格納された状態となるのでログイン後はセッションがいきている限り、User.find_by(id: session[:user_id])
でユーザーを簡単に取得することができます。
application_controller.rb
に以下を記述します。
class ApplicationController < ActionController::Base
helper_method :current_user
private
def current_user
@current_user || = User.find_by(id: session[:user_id]
if session[:user_id])
end
end
これでログインが成功するとsession[:user_id]にユーザーのidが入り、current_userでUserオブジェクトを取得できる状態になりました。ただ、このままだとログインしてなくてもどの機能も利用できる状態です。なので各種機能をログイン時のみ利用できるように制限させます。その前にログアウト機能を実装します。
ログアウト機能を実装する
ログアウト機能はログイン状態からログインしていない状態に変えるため、session[:user_id]にnilが入っている状態にします。
セッションからuser_idの情報だけをピンポイントで消す場合は以下のように記述します。
session.delete(:user_id)
セッション内の情報を全て削除するには以下のように記述します。
reset_session
それでは上記のような処理を実行するためのdestroyアクションをSessionsControllerに追加していきます。その前にまずはログアウトのためのルーティングを設定します。
(config/routes.rb)
Rails.application.routes.draw do
get '/login', to: 'sessions#new'
get '/login', to: 'sessions#create'
get 'logout', to: 'sessions#destroy'
...
end
次にコントローラーでアクションを追加します。(app/controllers/sessions_controller.rb)
def destroy
reset_sesion
redirect_to root_path, notice: 'ログアウトしました。'
end
最後にviewでログアウトのリンクを追加します。(app/views/layouts/application.html.erb)
<body>
<nav class="navbar-expand-md.navbar-light pt-1"
style="background-color: #58b2f4;">
<a class="navbar-brand ml-2 h1">IdeaBrite</a>
<ul class="navbar-nav ml-auto">
<% if current_user %>
<li class="nav-item">
<% link_to 'タスク一覧', task_path, class: 'nav-link' %>
</li>
<li class="nav-item">
<% link_to 'ユーザー一覧', admin_users_path,
class: 'nav-link' %>
</li>
<li class="nav-item">
<% link_to 'ログアウト', logout_path, method: :delete,
class: 'nav-link' %>
</li>
<% else %>
<li class="nav-item">
<% link_to 'ログイン', login_path, class: 'nav-link' %>
</li>
<% end %>
</ul>
</nav>
まず、ヘルパーで定義したcurrent_user
メソッドを利用して、ログインしている時はログアウトのリンクを表示します。
<li class="nav-item">
<% link_to 'タスク一覧', task_path, class: 'nav-link' %>
</li>
...
<li class="nav-item">
<% link_to 'ログアウト', logout_path, method: :delete,
class: 'nav-link' %>
</li>
ログインしていない時はログイン画面へのリンクを表示します。
<li class="nav-item">
<% link_to 'ログイン', login_path, class: 'nav-link' %>
</li>
これでログイン、ログアウトができました。ただ、現状ログイン時に一定の機能を使えるようにしてログアウト時に一定の機能を制限する重要な機能が実装できていないので次から作っていきます。