Railsでログイン機能とログアウト機能を実装する方法

User用のページを作成する

ユーザー一覧ページの作成

  • password,email,nameをもったUserテーブルを作成する
  • ユーザー一覧ページを作成する(get “users/index” = “users#index”)。 @user = User.all → htmlでeachして取り出す

ユーザー詳細ページの作成

  • ユーザー詳細ページを作成する(get “users/:id/” = “users#show”) 詳細ページでは詳細が表示されるようにする(emailや名前など)。コントローラーでfind_by()メソッドを使用してparams[:id]を取得

ユーザー新規登録ページの作成

必要なページは新規登録をうけつけるページとユーザーの情報を受け取り処理するページ。ユーザーからの情報を受け取るページはpostにしておく。

  • 新規登録ページを作成する(get “signup” => “users#new”)。ユーザー名、メールアドレス、パスワードを受け取る。フォームの値をうけとるにはform_tag()メソッドを使用する。
  • 受け取り先のurlは(post “users/create”, “users#create”)にしておき createアクションで新たにUserインスタンスを作成→データベースに保存→ユーザー一覧ページにリダイレクトさせる。ユーザーの詳細ページにリダイレクトさせたい場合はlidirect_to(“/users/#{@user.id}”)などする

新規登録が成功か失敗したかどうかの条件分岐
もし新規登録が成功したときは(if @user.save = true)flash[:notice]などを使用する。application.html.erb(親でflash[:notice])の記述必要あり。任意のページにリダイレクトさせる。失敗した場合はrender()メソッドを使用し元のページを再表示させる。このとき入力中の文字が消えないように、value属性にユーザーからの入力情報を設定しておく value=”<%= @user.name%>“など

def create
    @user = User.new(name: params[:name], email: params[:email])
    # 保存が成功したかどうかで条件分岐をしてください
    if @user.save
      flash[:notice] = "ユーザー登録が完了しました"
      redirect_to("/users/#{@user.id}")
    else
      render("users/new")
    end
  end

ユーザーの編集機能を作成する

ユーザーごとの編集ページをもたせる。ユーザー編集ページとはユーザーの情報を編集できるようにしたもの。(emaliやnameなど)。inputタグを用意し、emailや

  • ルーティング(get “users/:id/edit” => “users#edit”)
  • edit.html.erbはフォームの入力内容をform_tag()メソッドで送信する。送信先は(post “users/:id/update”, “users#update”)
  • フォームの値を受け取りデータベースに反映させる。
  • 保存が成功か失敗かで条件分岐させる

新規登録ではなく、編集なので新たにインスタンスを生成してデータベースには登録しない。ユーザーから受け取った情報をデータベースのカラムに再代入する。データベース保存の条件分岐のサンプル↓

def update
    @user = User.find_by(id: params[:id])
    @user.name = params[:name]
    @user.email = params[:email]
    if @user.save
      flash[:notice] = "ユーザー情報を編集しました"
      redirect_to("/users/#{@user.id}")
    else
      render("users/edit")
    end
  end

ユーザーの画像表示

データベースに画像名を保存するためのカラム追加からはじめる。まずマイグレーションファイルを作成する

  • ($rails g migration ファイル名(わかりやすい名前))
  • マイグレーションファイルのchangeメソッドに(add_column :テーブル名, :カラム名, :データ型)を記述→rails db:migrateで反映
  • 新規登録時(users#createアクション)のデフォルト画像を設定する。デフォルトの画像はpublicフォルダに入れておく。具体的にはインスタンス生成時にimage_nameカラムの値を指定

画像を表示させる

  • publicフォルダの画像をsrc属性に指定する際はpublicは記述せず/からはじめる。→ src=”/xxx/<%= user.image_name%>“
  • ユーザー一覧ページやユーザー詳細ページにimgタグを設置する。

画像表示のhtmlサンプルコード

<img src="/user_images/<%= user.image_name%>"><!--userは一覧ページの場合はeachからとっているため。詳細ページの場合は@userにする-->

ユーザーから画像をうけとりデータベースに登録と画像フォルダに保存

ユーザー情報を編集するupdateアクションや、新規登録時のcreateアクションで画像を受け取る

  • ユーザーから画像をうけとるにはinputタグの type属性にfileを指定する
  • またform_tag()メソッドの第二引数は{maltipart: true}に設定する
  • 画像をフォルダに保存するにはFile.binwrite(ファイル名, 画像名)メソッドを使用するが、このときファイル名はpublicからはじめる
  • ユーザーから画像を受け取ったことを判断する条件分岐はif params[:image]で行う

画像保存のアクションサンプルコード

  def update
    @user = User.find_by(id: params[:id])
    @user.name = params[:name]
    @user.email = params[:email]
    
    # 画像を保存する処理を追加してください
    if params[:image]
      #データベースの変更
      @user.image_name = "#{@user.id}.jpg"
      #画像フォルダへ保存
      image = params[:image]
      File.binwrite("public/user_images/#{@user.image_name}", image.read)
    end
    
    if @user.save
      flash[:notice] = "ユーザー情報を編集しました"
      redirect_to("/users/#{@user.id}")
    else
      render("users/edit")
    end
  end

ログイン機能の実装

ログイン機能の概要

  • ログインページの作成とログイン情報を受け取るためのpost urlを作成。
  • session変数の活用でログイン状態にする
  • ログイン状態を判定して表示させるコンテンツを変える
  • アクセス制限の実装
  • ユーザー自身の情報だけを編集できるようにする

ログインページの作成

ログイン機能はユーザーに属しているので、userコントローラーに作成する。まずはログインページを作成するためルーディング、アクション、ビューを作成する

  • ルーティング(get “login” => users#login_form)
  • htmlはinputタグのtype属性にpasswordを指定 mail用のinputタグも作成。
  • ログインページのリンクをヘッダーに追加
  • この時点でuserデータベースにpasswordカラムを追加してない場合はrailsコマンドで作成しておく
  • passwordカラムのバリデーションを忘れずに設定しておく
  • パスワードの追加テストなどはrails consoleで行う

ユーザーのログイン情報を受け取る(ログイン機能の実装)

  • emailやパスワードを受け取るためにinputタグのname属性を指定
  • 受け取りのルーティング(post “login” => “users#login”)
  • form_tag()メソッドでログイン情報をの受け取り先のURLを指定
  • ユーザーが存在するかしないかで処理をふりわける

ユーザーが存在するかどうかはfind_by()メソッドを用いて行う → User.find_by(email: params[:email], password: params[:password])などで行う。この機能の実装は情報の受け取り先(post “login”)で行う。

ログイン情報を受け取り処理するサンプルコード

 def login
    @user = User.find_by(email: params[:email], password: params[:password])
    if @user
      flash[:notice] = "ログインしました"
      redirect_to("/posts/index")
    else
      # @error_messageを定義してください
      @error_message = "メールアドレスまたはパスワードが間違っています"
      
      # @emailと@passwordを定義してください
      @email = params[:email]
      @password = params[:password]
      render("users/login_form")
    end
  end

@error_messageはログインに失敗したときにhtmlで表示させる。@error_message活用のhtmlサンプルコード

<% if @error_message %>
          <div class="form-error">
            <%= @error_message %>
          </div>
        <% end %>

ログインをブラウザに保持させるためのsessionを実装する

loginアクションにsessionを実装する。session[:user_id]変数を定義し、値はログインしたuserのidを指定する。

sessionのサンプルコード

 def login
    @user = User.find_by(email: params[:email], password: params[:password])
    if @user
      flash[:notice] = "ログインしました"
      redirect_to("/posts/index")
    else
      # @error_messageを定義してください
      @error_message = "メールアドレスまたはパスワードが間違っています"
      
      # @emailと@passwordを定義してください
      @email = params[:email]
      @password = params[:password]
      render("users/login_form")
    end
  end

ログアウト機能の実装

ログアウト機能はsessionをnilにすることで実現する。logout機能を実装するためにlogoutアクションを追加する。まずは、ルーティングとアクションとビューを準備

ログアウトアクションの実装

  • ルーティング(post “logout” => “users#logout”)
  • logoutアクションに session[:user_id] = nilを設定
  • ログアウトのリンクをlink_to()メソッドで作成。このときpostのurlにリンクさせるため、第三引数に{method: :post}を指定する

ログイン状態かログアウト状態かでページの表示を切り替える

ログインの状態でページ表示を切り替えるにはsessionを使う。session

ログイン/ログアウトの状態でページの表示を切り替えるサンプル

<ul class="header-menus">
        <% if session[:user_id] %>
          <li>
            現在ログインしているユーザーのid:
            <%= session[:user_id] %>
          </li>
          <!-- ログインしている状態でヘッダーに表示するHTMLを貼り付けてください -->
          <li>
            <%= link_to("投稿一覧", "/posts/index") %>
          </li>
          <li>
            <%= link_to("新規投稿", "/posts/new") %>
          </li>
          <li>
            <%= link_to("ユーザー一覧", "/users/index") %>
          </li>
          <li>
            <!-- ログアウト用のリンクを追加してください -->
            <%= link_to("ログアウト", "/logout", {method: :post})%>
          </li>
        <% else %>
          <!-- ログインしていない状態でヘッダーに表示するHTMLを貼り付けてください -->
          <li>
            <%= link_to("TweetAppとは", "/about") %>
          </li>
          <li>
            <%= link_to("新規登録", "/signup") %>
          </li>
          <li>
            <%= link_to("ログイン", "/login") %>
          </li>
        <% end %>
      </ul>

ログイン・ログアウトでhtmlの状態を切り替えるにはsessionを使うことでも可能だがapplication_controllerにbefore_actionを定義しておいてどこでも使えるようにしておく。before_actionはコントローラーの先頭に書くことで全アクションで共通の変数が利用できるようになる

applicaton_controller.erbのサンプルコード

class ApplicationController < ActionController::Base
  before_action:set_current_user
  
  def set_current_user
    @current_user = User.find_by(id: session[:user_id])
  end
end

新規登録時にログインさせる

新規登録時のアクションにログイン状態になるようにsession変数を定義する

  • 新規登録のときはインスタンスを生成するのでpasswordカラムの引数がなければ引数を持たせる
  • session[:user_id]を定義して@user.idを代入して新規登録時にログイン状態にする

ログイン・ログアウト状態でのアクセス制限の実装

ログアウト状態で指定のページにアクセスするのを防ぐには。リダイレクトさせるメソッドを用意して、before_actionに作成したメソッドを設定する。指定したアクションのみメソッドが呼ばれるようにbefore_actionにメソッドを設定する際は only 記述する

only の記述の仕方↓サンプル

before_action :authenticate_user, {only: [:index, :show, :edit, :update]}

リダイレクトメソッドのサンプル @current_user = nilとすることでログアウト状態を判定する

  def authenticate_user
   #ログアウト状態だったら
    if @current_user == nil
    flash[:notice] = "ログインが必要です"
    redirect_to("/login")
  end
  end

ログイン状態でのアクセス制限も同様。リダイレクトさせるメソッドを用意して、before_actionに登録する。

def forbid_login_user
    #ログイン状態だったら
    if @current_user
      flash[:notice] = "すでにログインしています"
      redirect_to("/posts/index")
    end
  end

ユーザー自身の情報だけを編集できるようにする

メソッドを定義し ログイン状態のユーザーと@user.idを条件判定を行いウェブページの表示を制限する。

例として@current_user = User.find_by(id: session[:user_id])となっている状態でウェブページの表示を制限するサンプルコード↓

<%if @current_user.id == @user.id%>
      <%= link_to("編集", "/users/#{@user.id}/edit") %>
      <%end%>

ログインユーザー以外の情報を編集しようとしたときに制限をかける

編集のurlを直接指定した場合に他のユーザーの情報が編集されるのを防ぐ。

ログインしているユーザーのidと編集したいユーザーのidが違う場合にアクセス制限をかけるサンプルコード

def ensure_correct_user
    if @current_user.id != params[:id].to_i
      flash[:notice] = "権限がありません"
      redirect_to("/posts/index")
    end
  end

どこにアクセス制限をかけるかはbefore_actionのonlyで決める。

よく考える必要がある。