Railsでログイン機能をgemを使わずに実装する方法

✏️️ 2019/08/10 👍️2019/08/16 🔗

Railsでの認証機能はDoorkeeperDeviseが有名ですが、今回はgemを利用せずに実装します。

User Modelを作成

カラム名はname, email, password_digestとします。

bin/rails g model user name email password_digest

マイグレーションファイルを修正

それぞれのcolumnをnull: false とします。また、email カラムはuniqueにします。

# db/migrate/2019_create_users.rb

class CreateUsers < ActiveRecord::Migration[6.0]
  def change
    create_table :users do |t|
      t.string :name, null: false
      t.string :email, null: false
      t.string :password_digest, null: false

      t.timestamps
      t.index :email, unique: true
    end
  end
end

マイグレーションを実行

マイグレーションを実行して、usersテーブルを作成します。

bin/rails db:migrate

パスワードを暗号化する

Railsのhas_secure_passwordメソッドを利用します。has_secure_passwordを利用して、パスワードをハッシュ化するにはbcryptというgemが必要です。Gemfileにbcryptを追加します。

# Gemfile
# Use Active Model has_secure_password
gem 'bcrypt', '~> 3.1.7'

bundleを実行します。

bundle

User Modelにhas_secure_passwordを追加します。

# app/models/user.rb
class User < ApplicationRecord
  has_secure_password
end

Userを作成する

irbからUserを作成します。has_secure_passwordを利用すると、password属性とpassword_confirmationがModelに追加されます。passwordpassword_confirmationが一致する場合password_digestpasswordがハッシュされた値が保存されます。

> User.create(name: 'user01', email: 'user01@test.com', password: 'pass', password_confirmation: 'pass' )
=> #<User id: 1, name: "user01", email: "user01@test.com", password_digest: [FILTERED], created_at: "2019", updated_at: "2019">

> User.first.password_digest
=> "$2a$12$PKEWTSHjBXChiJbg8Badr.Y84gKqWQRuo6mQMjN1llRfe17IdXSG."

ログイン機能を実装

SessionControllerを作成し、ログイン機能を作成します。

bin/rails g controller Sessions new

Routingを修正

/loginでログインSessions Controllerのnew Actionが呼ばれるようにします。

# config/route.rb
Rails.application.routes.draw do
  get :login, to: 'sessions#new'
end

ログインフォームを作成

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

# app/views/sessions/new.html.erb
<h1>Sessions#new</h1>
<%= form_with scope: :session, local: true do |f| %>
  <div>
    <%= f.label :email, 'Mail Address' %>
    <%= f.text_field :email, id: 'session_email' %>
  </div>
  <div>
    <%= f.label :password, 'Password' %>
    <%= f.password_field :password, id: 'session_password' %>
  </div>
  <%= f.submit 'login' %>
<% end %>

sessionを作成

Routingを修正します。

# config/routes.rb
Rails.application.routes.draw do



  post :login, to: 'sessions#create'
end

sessionを作成するcreate Actionを作成します。

# app/controllers/sessions_controller.rb
class SessionsController < ApplicationController



  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: 'Login succeeded'
    else
      render :new
    end
  end

  private

  def session_params
    params.require(:session).permit(:email, :password)
  end
end

authenticate メソッドは、has_secure_passwordを追加したときに追加された認証のためのメソッドで、引数で受け取った値とpasswoed_digestの値を比較します。一致していればtrue, 不一致ならばfalseを返します。

session[:user_id] = user.id セッションにuser.idを格納します。
よって、user.idがセッションに含まれている場合ログイン状態。そうでなければ未ログインとなります。

ログイン状態を取得するメソッドを作成

ログイン状態かどうかを判定するメソッドを作成します。

# app/controllers/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

helper_methodを利用して、viewからもcurrent_userメソッドを利用できるようにします。
||=は左辺が未定義または偽なら右辺の値を代入するという意味です。

ログアウト機能を実装

セッションにuser.idを格納してあれば、ログイン済みとなるので、セッションのuser.idを削除すればログアウトとなります。

Routingを修正します。

# config/routes.rb
Rails.application.routes.draw do



  delete :logout, to: 'sessions#destroy'
end

sessionを削除するdestroy Actionを作成します。

# app/controllers/sessions_controller.rb
class SessionsController < ApplicationController



  def destroy
    reset_session
    redirect_to root_path, notice: 'Logout Succeeded'
  end



end

reset_sessionメソッドはセッション情報を削除するメソッドです。

ログイン・ログアウトボタンを作成

最後にログイン・ログアウトボタンを作成します。

app/views/layouts/application.html.erb

・
・
・
  <body>
    <% if current_user %>
      <%=  link_to 'logout', logout_path, method: :delete %>
    <% else %>
      <%=  link_to 'login', login_path %>
    <% end %>
    <%= yield %>
  </body>
・
・
・

参考

akito
日本のスタートアップで主にRuby on Railsを使ってプロダクト開発をしています。