deviseを使わないサインアップ/サインイン機能の実装

TECH::CAMPに通い始めてはや5ヶ月。

卒業までの期間も残すところあと1ヶ月となりました。

 

個人アプリ作成の合間の時間で作成している、就職活動用の個人アプリの作成において、

deviseを使わずにサインアップ・サインイン機能を実装してみようと思いました。

deviseは便利ですが、サインアップ・サインイン機能どういう仕組みで成り立っているのかがいまいちわかっていないため、

自分で実装してみよう!と思い立ちました。

 

参考にさせていただいた記事

https://qiita.com/tmzkysk/items/12c3392dff6da1c87fdf

https://qiita.com/d0ne1s/items/7c4d2be3f53e34a9dec7

 

  

テーブルとモデルを作成する

class CreateUsers < ActiveRecord::Migration[5.2]
def change
create_table :users do |t|
t.string :username, null: false
t.string :email, null: false, unique: true
t.string :password_digest, null: false
t.timestamps
end
end
end

password_digestは、パスワードを暗号化するためのカラム名です。

必ずpasswordではなくpassword_digestとするようにしましょう。

 

次はモデルです。

class User < ApplicationRecord
has_many :projects
before_save {self.email = email.downcase}

validates :username, presence: true
validates :email,
presence: true,
uniqueness: {case_sensitive: false},
format: {with: /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i}
has_secure_password
end

保存する時にemailを全て小文字に変換します。

has_secure_password と記載(gem 'bcrypt'がインストールされていれば使えるメソッドです)することで、

・viewで:passwordと:password_confirmationという属性が使えるようになり、

 パスワード入力・確認用の入力項目を作れるようになる。

 password_confirmationはテーブルには保存されないため、

 カラムがなくてもエラーは出ません。

・登録したパスワードを「password_digest」カラムに保存する際に

 暗号化されるため、安全性が保たれる。

・authenticateメソッドが使えるようになる。

 ログイン機能の実装の際に、

 ユーザーが正しいパスワードを入力しているのかどうか判定できるメソッドです。

 

 

 サインアップ・サインインページのマークアップとルーティング

f:id:atsukofu:20200806213547p:plain

f:id:atsukofu:20200806213630p:plain

 

 

サインイン時には、emailとパスワードの組み合わせが登録時のものと一致すれば、

サインインができるという仕組みです。

 

ルーティングはこのようにしています。

Rails.application.routes.draw do
resources :users, only: [:new, :create, :destroy]
resources :sessions, only: [:new, :create, :destroy]
end

users: :new, :create → ユーザーの新規登録

users: :destroy  →  ユーザーの削除

sessions: :new, create  →  ログイン機能(セッションのスタート)

sessions: :destroy  →  ログアウト機能(セッションの終了)

 

 

コントローラーの作成

class UsersController < ApplicationController
def new
@user = User.new
end

def create
@user = User.new(user_params)
if @user.save
session[:user_id]=@user.id
session[:user_name]=@user.username
redirect_to root_path
else
render action :new
end
end

private

def user_params
params.require(:user).permit(:username, :email, :password, :password_confirmation)
end
end

 ここでdeviseでは使わない、session[:user_id]や、session[:user_name]が出てきました。

deviseではログインしているユーザーを「current_user」というメソッドで自動的に取得することができますが、

今回はcurrent_userが使えないため、

session[:user_id]などに、@userの持つ情報をそれぞれ代入しています。

(今回はビューで@userを使うので、userではなく@userにしています。)

 

次にサインイン時のコントローラー

SessionsControllerです。

class SessionsController < ApplicationController
def new
end

def create
user = User.find_by(email: session_params[:email])
if user && user.authenticate(session_params[:password])
session[:user_id] = user.id
session[:user_name] = user.username
redirect_to root_path, notice: 'ログインしました'
else
session[:email] = nil
render :new, notice: 'ログインできませんでした'
end
end

def destroy
reset_session
redirect_to new_session_path, notice: 'ログアウトしました'
end

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

サインインの仕組みとしては、

まず、入力したemailアドレスに一致するUserをuserに代入します。

そして

・userが存在すること(=入力したemailを持つUserが存在すること)

・userのもつpasswordが入力したpasswordと一致すること

以上の2つを満たすUserを探し当てることができれば、

session[:user_id]とsession[:user_name]に探し当てたuserの情報をそれぞれ代入します。

最後にroot_pathへ移動すれば、ログイン中のユーザー名をsession[:user_name]で表示させ、ログインできていることを確認します。

f:id:atsukofu:20200806224425p:plain

「3番」という名前のユーザーでログインしました。

しっかりユーザー名が表示されています!

 

 

まとめ

コードだけ見るとすごく簡単なのですが、

まず仕組みを理解するところからなので、これだけで1日かかってしまいました。

sessionの仕組みはPHPを触った時に少し学んだため、理解自体はそこまで時間が掛からなかったと思います。

 

一番引っかかったのは、コントローラーで

user.find_by(email: session_params[:email])とすることろです。

emailの値は「params」ではなく「session_params」に入っているのか!

と理解するまでに時間を要しました・・

 

次はサインインしているユーザー以外がアクセスできないようにするなど

バリデーションを加えて行きたいですが、

次回以降でアウトプットして行こうと思います。