Rails

railsでログイン・ログアウト機能を作る

仕様書

  • ユーザーのログイン機能
  • ログインした後の状態管理
  • ログアウト機能

ログインページのview作成

ルーティングの設定

routes.rb
get "login" => "users#login_form"

コントローラの設定

users_controller.rb
  def login_form
  end

DBにパスワードカラムの追加

マイグレーションファイルの設定

test
rails g migration add_password_to_users

コマンドからmigrationファイルを作成し、マイグレーションファイルを下記に編集。

rei
class AddPasswordToUsers < ActiveRecord::Migration[5.0]
  def change
    add_column :users, :password, :string
  end
end

バリデーションの設定

users.rb
class User < ApplicationRecord
  validates :name, {presence: true}
  validates :email, {presence: true, uniqueness: true}
  # passwordカラムにバリデーションを設定してください
  validates :password, {presence: true}
end

passwordに空欄だとはじくバリデーションをmodelのuser.rbに記述。

ログインフォームの作成

ルーティングの設定

routes.rb
post "login" => "users#login"

DBに変更を与えるのでpostで設定

アクションの設定

rei
  def login
  end

中身は空だがusers#loginに対応するアクションを設定。

viewにおけるフォームの設定

<%= form_tag("/login") do %>
 <p>メールアドレス</p>
  <!-- name属性を追加してください -->
  <input name ="email">
  <p>パスワード</p>
  <!-- name属性を追加してください -->
  <input type="password" name ="password">
  <input type="submit" value="ログイン">
  <!-- form_tag用のendを追加してください -->
 <% end %>

ルーティングはroutes.rbでは最初に/が無いが他のファイルで設定する場合は/で始める。ルートは基本的にはroutes.rbで確認すると分かる。

このviewで/loginに値を送信するフォームを作成。inputにnameで値を設定。

ユーザーを特定する

ログインしたユーザーを特定し、そのユーザーのページを表示するための機能。

users_controller.rb
  def login
    @user = User.find_by(email: params[:email], password: params[:password])
    if @user
      flash[:notice] = "ログインしました"
      redirect_to("/posts/index")
    else
      render("users/login_form")
    end
  end

User.find_by(email: params[:email], password: params[:password])でpassword、email両方一致した場合のみ引っ張ってくる。

redirect_toはURLを送って問い合わせるため基本はroutes.rbのURLで良い。ただrenderはviewの相対パスに直接問い合わせを行う。users/login_formもviewをhomeにした時の相対パス。なので最初に/がない。

特定したユーザーが登録済みユーザーじゃなかった場合

test
  def login
    @user = User.find_by(email: params[:email], password: params[:password])
    if @user
      flash[:notice] = "ログインしました"
      redirect_to("/posts/index")
    else
      @error_message = "メールアドレスまたはパスワードが間違っています"
      @email = params[:email]
      @password = params[:password]

      render("users/login_form")
    end
  end

@userにもしなにも値が無かった場合、elseでeroorメッセージを流し、renderでlogin_formに流す。

login_form.html.erb
<%= form_tag("/login") do %>
 <p>メールアドレス</p>
 <input name="email" value="<%= @email %>">
 <p>パスワード</p>
 <input type="password" name="<%= @password %>">
 <input type="submit" value="ログイン">
<% end %>

初期値に前に登録した値を持ってくる。

ログインしたユーザー情報の保持(状態管理)

user_controller.rb
  def login
    @user = User.find_by(email: params[:email], password: params[:password])
    if @user
      session[:user_id] = @user.id

      flash[:notice] = "ログインしました"
      redirect_to("/posts/index")
    else
      @error_message = "メールアドレスまたはパスワードが間違っています"
      @email = params[:email]
      @password = params[:password]
      render("users/login_form")
    end
  end

sessionにuser_idを入れる。viewに

application.html.rb
<% if session[:user_id] %>
 <li>
  現在ログインしているユーザーのid: <%= session[:user_id] %>
 </li>
<% end %>

session[:user_id]は@つけなくてもそのままviewに表示できる。

ログアウト機能

ログアウトのメソッド

ログアウトはセッションにnilを代入することで行う。この時ルーティングはgetではなく、postを使う。

  • get・・・データベースを変更しない場合
  • post・・・データベース、sessionの値を変更する場合
rei
session[:user_id] = nil

controllerから権限の必要なページを限定する

before_actionで制限

rei
before_action: メソッド名,{only:[:アクション名,:アクション名]}

アクセス制限

非ログインユーザーに対してのアクセス制限

ログインしていないユーザーがURLを直接叩いても見れないようにアクセス制限をかけよう。application_controllerにforbid_login_userメソッドというログインを禁じるものを作り、各controllerに継承させる。

ensure_correct_userメソッドというログインユーザーと編集しようとしているユーザーのidが一致しない場合にアクセスを拒否するメソッドを作成し、before_actionで複数のアクションに用いる。

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

params[:id]で取得できる値は文字列のため.to_iメソッドを使って数字に変換する。

リンクなどの非表示

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

上記でuser.idと現在ログインしているuser.idが違うと表示されないようにする。

他のユーザーの情報を編集できないように制限をかける

あとで追記