基本的なログイン機構 | rails チュートリアル 12

基本的なログイン機構の勉強メモ

目的

ユーザーがログインやログアウトを行えるようにする

このシステムの基盤が出来上がったら、ログイン済みのユーザー (current user) だけがアクセスできるページや、扱える機能などを制御していくことが可能。

例えば、以下のような機能を作成できる。

  • ログインしたユーザーだけがユーザーの一覧ページに移動できる
  • ログイン済みかどうかでヘッダー部分を切り替える
  • 正当なユーザーだけが自分のプロフィール情報を編集できるようにする。
  • 管理者だけが他のユーザーをデータベースから削除できるようにする。
  • 他のユーザーをフォローする機能

セッション

ユーザーログインの必要なWebアプリケーションでは、セッションと呼ばれる半永続的な接続をコンピュータ間に別途設定することが可能。

Railsでセッションを実装する方法として最も一般的なのは、cookiesを使用する方法。

cookiesは、あるページから別のページに移動した時にも破棄されない。

sessionというRailsのメソッドを使用して一時セッションを作成する。

Sessionsコントローラ

さっそくコントローラーを作成する。

$ bundle exec rails generate controller Sessions new
Running via Spring preloader in process 42850
      create  app/controllers/sessions_controller.rb
       route  get 'sessions/new'
      invoke  erb
      create    app/views/sessions
      create    app/views/sessions/new.html.erb
      invoke  test_unit
      create    test/controllers/sessions_controller_test.rb
      invoke  helper
      create    app/helpers/sessions_helper.rb
      invoke    test_unit
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/sessions.coffee
      invoke    scss
      create      app/assets/stylesheets/sessions.scss

ルーティングを修正

  # get 'sessions/new'
  get    '/login',   to: 'sessions#new'
  post   '/login',   to: 'sessions#create'
  delete '/logout',  to: 'sessions#destroy'

html.erbを修正

<% provide(:title, "Log in") %>
<div class="page_mainvisual">
  <div class="container">
    <h1>Log in</h1>

    <%= form_for(:session, url: login_path) do |f| %>

      <table>
        <tbody>
          <tr>
            <th><%= f.label :email %></th>
            <td><%= f.email_field :email, class: 'form-control' %></td>
          </tr>
          <tr>
            <th><%= f.label :password %></th>
            <td><%= f.password_field :password, class: 'form-control' %></td>
          </tr>
        </tbody>
      </table>
      <p><%= f.submit "Log in", class: "btn btn-primary" %></p>
    <% end %>

    <p>New user? <%= link_to "Sign up now!", signup_path %></p>
</div>

ユーザーの検索と認証、エラーメッセージ

初に最小限のcreateアクションをSessionsコントローラで定義し、空のnewアクションとdestroyアクションもついでに作成する。

class SessionsController < ApplicationController

  def new
  end

  def create
    render 'new'
  end

  def destroy
  end

end

また、デバッグをしやすくするようにするため、以下のコードも記述

    <%= debug(params) if Rails.env.development? %>
  </body>
</html>

上記のコードを記述後、メールアドレスとパスワードを入力せずにログインボタンを押すと、
ページ下部にlogが表示されます。

--- !ruby/object:ActionController::Parameters
parameters: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
  utf8: "✓"
  authenticity_token: uBHrKl0/r40XidpxVNPLBK68ed+R3JIgcFfku+ZYoshtsyH/F4dm/+57GcfaxYjrqXdFXZe7acWcUR7Ki4+T/g==
  session: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
    email: ''
    password: ''
  commit: Log in
  controller: sessions
  action: create
permitted: false

このlogに、

{ session: { password: "", email: "" } }

# controllerでは、以下の記述で、それぞれの値が取得可能。
params[:session][:email]
params[:session][:password]

html.erbでは、

<%= params[:session][:email] %>
<%= params[:session][:password] %>

これで表示することが可能でもある。

これらを使用し、認証のシステムを作成する。

また、Active Recordが提供するUser.find_byメソッド、
has_secure_passwordが提供するauthenticateメソッドも使用。
※ authenticateメソッドは認証に失敗したときにfalseを返す。

とうことでコントローラーを修正する。

class SessionsController < ApplicationController

  def new
  end

  def create
    user = User.find_by(email: params[:session][:email].downcase)
    if user && user.authenticate(params[:session][:password])

    else
      flash.now[:danger] = 'Invalid email/password combination'
      render 'new'
    end
  end

  def destroy
  end

end

ログイン

どこでもセッションが使えるように、application_controller.rbを修正する

class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  include SessionsHelper
end

log_inメソッド

Railsで事前定義済みのsessionメソッドを使用して、単純なログインを行えるようにする。

module SessionsHelper

  # 渡されたユーザーでログインする
  def log_in(user)
    session[:user_id] = user.id #sessionメソッドはハッシュのように扱える
  end
end
class SessionsController < ApplicationController

  def new
  end

  def create
    user = User.find_by(email: params[:session][:email].downcase)
    if user && user.authenticate(params[:session][:password])
      log_in user      # 追記
      redirect_to user # 追記
    else
      flash.now[:danger] = 'Invalid email/password combination'
      render 'new'
    end
  end

  def destroy
  end

end

現在のユーザー

以下のコードで、ログインしているユーザー名をが取得できるようにする。

<%= current_user.name %>

そのために、sessions_helper.rbを修正

module SessionsHelper

  # 渡されたユーザーでログインする
  def log_in(user)
    session[:user_id] = user.id #sessionメソッドはハッシュのように扱える
  end

  # 追記
  # 現在ログイン中のユーザーを返す (いる場合)
  def current_user
    @current_user ||= User.find_by(id: session[:user_id])
  end
end

また、ログインしているかどうかで、リンクを変更できるように、sessions_helper.rbを修正する

module SessionsHelper

  # 渡されたユーザーでログインする
  def log_in(user)
    session[:user_id] = user.id #sessionメソッドはハッシュのように扱える
  end

  # 現在ログイン中のユーザーを返す (いる場合)
  def current_user
    @current_user ||= User.find_by(id: session[:user_id])
  end

  # ユーザーがログインしていればtrue、その他ならfalseを返す
  def logged_in?
    !current_user.nil?
  end
end

その後、html.erbを修正

<li><%= link_to "Log in", login_path %></li>
<% if logged_in? %>
  <li><%= link_to "Users", '#' %></li>
  <li><%= link_to "Profile", current_user %></li>
  <li><%= link_to "Settings", '#' %></li>
  <li><%= link_to "Log out", logout_path, method: :delete %></li>
<% else %>
  <li><%= link_to "Log in", login_path %></li>
<% end %>

引き続き、modelsを修正する。

class User < ApplicationRecord
  before_save { self.email = email.downcase }
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i

  # micropostと紐付ける
  has_many :microposts
  # 未入力を受け付けなくする
  validates :name,  presence: true, length: { maximum: 50 }
  validates :email, presence: true, length: { maximum: 255 },
                    format: { with: VALID_EMAIL_REGEX }, #メールのバリデート
                    uniqueness: { case_sensitive: false }

  has_secure_password
  validates :password, presence: true, length: { minimum: 6 } #パスワードの最小文字数も設定

  # 渡された文字列のハッシュ値を返す
  def User.digest(string)
    cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
                                                  BCrypt::Engine.cost
    BCrypt::Password.create(string, cost: cost)
  end
end

また、users_controller.rbも修正

  def create
    @user = User.new(user_params)

    respond_to do |format|
      if @user.save
        format.html {
          redirect_to @user,
          flash: { success: 'User was successfully created.'}
        }
        format.json { render :show, status: :created, location: @user }
        log_in @user # 追記
      else
        format.html { render :new }
        format.json { render json: @user.errors, status: :unprocessable_entity }
      end
    end
  end

ここで、「log_in @user」があれば、新規ユーザー化した瞬間にログインが行われる。

ログアウト

ユーザーが明示的にログアウトするまではログイン状態を保てなくてはならない。

sessions_helper.rbを修正する。

module SessionsHelper

  # 渡されたユーザーでログインする
  def log_in(user)
    session[:user_id] = user.id #sessionメソッドはハッシュのように扱える
  end

  # 現在ログイン中のユーザーを返す (いる場合)
  def current_user
    @current_user ||= User.find_by(id: session[:user_id])
  end

  # ユーザーがログインしていればtrue、その他ならfalseを返す
  def logged_in?
    !current_user.nil?
  end

  # 現在のユーザーをログアウトする
  def log_out
    session.delete(:user_id)
    @current_user = nil
  end
end

ここで定義したlog_outメソッドは、Sessionsコントローラのdestroyアクションでも同様に使っていく。

class SessionsController < ApplicationController

  def new
  end

  def create
    user = User.find_by(email: params[:session][:email].downcase)
    if user && user.authenticate(params[:session][:password])
      log_in user
      flash[:success] = 'log in'
      redirect_to user
    else
      flash.now[:danger] = 'Invalid email/password combination'
      render 'new'
    end
  end

  def destroy
    log_out
    flash[:success] = 'log out'
    redirect_to root_url # ログアウトしたらサイトトップへリダイレクト
  end

end

herokuにup

ここまでで、簡単なログイン、ログアウトができたので、herokuにupします。

git add ., git commit , git ch master, git merge basic-login, git push heroku master をしていく。

つぎは、第9章 発展的なログイン機構を勉強する。

コメント

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

内容に問題なければ、下記の「コメントを送信する」ボタンを押してください。


  1. KATOON.NET
  2. TRASH
  3. 基本的なログイン機構 | rails チュートリアル 12