Yuki Matsumoto

To be engneer soon / Ruby on Rails

ログイン機能を作成する(パート4:ログイン機能実装する)

2019-03-10 Yuki MatsumotoRuby on Rails

ここからは念願のログイン機能を実装していきます。ログイン機能はログインするためのフォーム画面を表示し、送られてきた情報を元にユーザーを認証させてあげます。またログアウトもできるようにします。

ログインフォームを作成する

ここからはいつも通りの流れです。まずはログイン機能に必要なコントローラーを作成します。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>

これでログイン、ログアウトができました。ただ、現状ログイン時に一定の機能を使えるようにしてログアウト時に一定の機能を制限する重要な機能が実装できていないので次から作っていきます。