目次
開発環境
- Ruby:version 3.1.2
- Ruby on Rails:version 7.0.4
- Visual Studio Code:version 1.73.0
- OS:Windows10
Ruby on RailsでGemなしで自作ログイン機能の実装手順
Ruby on Railsのログイン機能は、ユーザーに適したサービスを提供するために様々なWebアプリケーションで実装されています。
Ruby on Railsのログイン機能については、deviseというgemファイル有名ですが、今回はdeviseなしでログイン機能を自作で実装しています。
今回はメモアプリの作成で使用した「ログイン機能」のコードを用いて解説していきます。
ログイン機能のユーザーモデルの作成
まずはログイン機能で使用するユーザーモデルを作成します。
1 | rails g model User |
次にユーザーモデルのマイグレーションファイルに必要な「名前、メールアドレス、パスワード」のカラムを追加していきます。
1 2 3 4 5 6 7 8 9 10 | class CreateUsers < ActiveRecord::Migration[7.0] def change create_table :users do |t| t.string :name t.string :email t.string :password t.timestamps end end end |
上記の記述が完了したら、以下のコマンドを叩いてマイグレーションファイルを反映させます。
1 | rails db:migrate |
今回はユーザー機能の作成は省略しますため、ダミーデータをコンソールで挿入していきます。
1 | rails c |
1 | User.create(name: "テストユーザー", email: "test@example.com", password: "12345678") |
パスワードについては、bcryptというgemファイルを使用して暗号化するのがおすすめですが、今回は省略しています。
また、今回は省略していますが、ユーザー機能を作成するときにはそれぞれのカラムに適したバリデーションをかけていきましょう。
ログイン機能のセッションコントローラーの作成
次にログイン機能で使用するセッションコントローラーを作成します。
1 | rails g controller sessions |
ログイン機能のルーティングの作成
次にログイン機能に必要な「ログイン画面の表示、ログイン処理、ログアウト処理」のルーティングを作成していきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 | Rails.application.routes.draw do root :to => 'memos#index' get 'login', to: 'sessions#new', as: 'new_sessions' post 'login', to: 'sessions#create', as: 'create_sessions' delete 'logout', to: 'sessions#destroy', as: 'destroy_sessions' resources :memos, only: [:index, :update, :destroy, :create] post 'ajax_memos_create', to: 'memos#ajax_create', as: 'ajax_memos_create' get 'search', to: 'memos#search', as: 'search' # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html # Defines the root path route ("/") # root "articles#index" end |
それぞれnewがログイン画面の表示、createがログイン処理、destroyがログアウト処理のルーティングになります。
ログイン画面のビューの作成
次にログイン画面のビューを作成していきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 | <div class="wrapper"> <div class="container"> <h2>ユーザーログイン</h2> <%= form_with url: create_sessions_path, local: true, method: :post do |f| %> <%= f.label :email, "Email" %> <%= f.email_field :email, class: "form-box" %> <%= f.label :password, "Password" %> <%= f.password_field :password, class: "form-box" %> <%= f.submit "ログイン", class: "btn" %> <% end %> </div> </div> |
ログインに必要な情報であるEmailとPasswordを入力する画面です。
Webサービスによってはユーザー名とパスワードにしても良いですが、今回はEmailとPasswordでユーザーを判定するように処理をしていきます。
ログイン機能に必要な処理をセッションコントローラーに記述
次にログイン機能に必要な処理をセッションコントローラーに記述していきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | class SessionsController < ApplicationController before_action :logged_in?, only: [:new, :create] def new end def create @user = User.find_by(email: params[:email], password: params[:password]) if @user session[:user_id] = @user.id flash[:notice] = "ログインに成功しました。" redirect_to memos_path else flash[:alert] = "ログインに失敗しました。" render action: "new" end end def destroy session[:user_id] = nil redirect_to new_sessions_path end private def logged_in? if session[:user_id] @user = User.find(session[:user_id]) flash[:notice] = "すでにログインしています。" redirect_to memos_path end end end |
createアクションでは、入力されたEmailとPassWordでユーザーモデルに保存されたユーザーレコードを探してきています。
もし、ユーザーが存在していたらsessionというメソッドでユーザーIDを発行し、セッションに保存したユーザーIDをクライアントのクッキーへ渡しています。
これにより、セッションで保存したユーザーIDとユーザーのクッキーに保存されたユーザーIDを照合してユーザー認証することができます。
また、EmailとPassWordでDBからユーザーを探せなかった場合、ログインできないようにしています。
destroyアクションではsessionメソッドでユーザーIDをnilとしてクライアントのクッキーへ渡すことにより、ログアウト状態にすることができます。
そのため、作成したlogged_in?というメソッドは、sessionメソッドでユーザーIDが存在していればログイン状態だと判断することができます。
また、それぞれの処理にフラッシュメッセージやエラーメッセージ、ログイン後の遷移先もどのようにするか検討してみてもらえればと思います。
ログイン機能によるアクセス制限をmenoコントローラーに記述
次にログイン機能によるアクセス制限をmenoコントローラーに記述していきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | class MemosController < ApplicationController before_action :current_user def index @memo_new = Memo.new @memos = Memo.all end def create @memo_new = Memo.new(memos_params) @memos = Memo.all if @memo_new.save redirect_to root_path else render action: "index" end end def update @memo = Memo.find(params[:id]) @memo.update(memos_params) redirect_to root_path end def destroy @memo = Memo.find(params[:id]) @memo.destroy redirect_to root_path end def ajax_create @memo_new = Memo.new(memos_params) @memos = Memo.all if @memo_new.save flash.now[:notice] = "メモの保存に成功しました。" else flash.now[:alert] = "メモの保存に失敗しました。" end end def search seach_word = params[:word] @memos = Memo.where("title LIKE ? or description LIKE ?", "%#{seach_word}%", "%#{seach_word}%") if @memos.count > 0 flash.now[:notice] = "#{@memos.count}件のメモが見つかりました。" else flash.now[:alert] = "#メモが見つかりませんでした。" end end private def memos_params params.require(:memo).permit(:title, :description) end def current_user if session[:user_id] @user = User.find(session[:user_id]) else flash[:alert] = "ログインする必要があります。" redirect_to new_sessions_path end end end |
current_userメソッドでユーザーがログインしていない状態だった場合、リダイレクトで遷移先をログインページに飛ばす処理をしています。
before_actionでメモコントローラーが呼び出された場合は必ず実行するようにしておけば、全てのメモコントローラーにアクセス制限をかけるという意味になります。
もし、適用したいアクションを指定したい場合は、exceptやonlyで指定しましょう。
ヘッダーにログインとログアウトボタンを作成
次にヘッダーにログインとログアウトボタンを作成します。
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | <body> <div class="wrapper header-color"> <div class="container flex-between"> <div class="header-left"> <h1>MemoApp</h1> </div> <div class="header-right"> <% if @user.nil? %> <%= link_to "ログイン", new_sessions_path, method: :get %> <% else %> ログインユーザー:<%= @user.name %>さん <%= link_to "ログアウト", destroy_sessions_path, method: :delete %> <% end %> </div> </div> </div> <div id="notice"> <%= render "layouts/notice" %> </div> |
もし、ユーザーがログインしていればログアウトボタンを表示、ログアウトしていればログインボタンを表示するという条件分岐になります。
@userにはログインユーザーの情報が代入されているため、もしログインしていればログインユーザーのみユーザー名を表示することで、ログイン状態かわかりやすいようにすることもできます。
ヘッダーのメニューボタンのデザインを調整
最後にヘッダーのメニューボタンのデザインを調整します。
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | .header-color { background-color: #4169e1; } .header-left { } .header-right { color: #ffffff; display: inline-block; line-height: 60px; } .header-right a{ text-decoration: none; color: inherit; } .green-color{ background-color: green; } .red-color{ background-color: red; } .notice-text { color: #ffffff; font-weight: bold; text-align: center; padding: 10px 0; } h1 { font-size: 36px; color: #ffffff; } h2 { font-size: 24px; } h3 { font-size: 24px; } .wrapper { width: 100%; } .container { margin: 0 auto; width: 100%; max-width: 1170px; } .col-box-1{ width: 100%; padding: 6px; } .col-box-2{ width: 50%; padding: 6px; } .col-box-3{ width: 33.3%; padding: 6px; } .col-box-4{ width: 25%; padding: 6px; } .flex-start { display: flex; flex-wrap: wrap; justify-content: start; } .flex-between { display: flex; flex-wrap: wrap; justify-content: space-between; } |
今回はヘッダーにflexでbetweenを適用することにより、左右に展開することができます。
上記のコーディングは簡易的なデザインなので、好みでデザインを調整してもらえればと思います。
おわりに
Ruby on Railsで自作ログイン機能の実装について解説してきましたが、いかがだったでしょうか。
最初はログイン機能を自作で実装するのはとても難しく感じるかもしれませんが、自作ログイン機能が実装できれば基本的なクッキーとセッションの理解も深くなっていくかと思います。
是非、ユーザーがWebアプリケーションを使いやすいようにするためにも、ログイン機能の実装にチャレンジにしてみてください。