deviseを使ったユーザー認証はとても便利ですが、カスタマイズをしたいときや、APIモードで使う時には不便なことがあります。
そこで今回は、deviseを使わない自前のユーザー認証を実装します。
下準備
1. Gemfileに使用するGemを追加します
(Gemfileの中でコメントアウトしてあるかと思います。)
gem "bcrypt", "~> 3.1.7"
2. bundle install
します
3. Userモデルを作成します
$ rails g model user
20240712000000_create_users.rb
class CreateUsers < ActiveRecord::Migration[7.1] def change create_table :users do |t| t.string :email, null: false t.string :password_digest t.timestamps end end end
4. passwordをハッシュ化する準備をします
app/models/user.rb
class User < ApplicationRecord has_secure_password end
サインアップの実装
1. ルーティングを設定する
config/routes.rb
get '/sign_up', to: 'sign_up#new' post '/sign_up', to: 'sign_up#create' get '/sign_up/thanks', to: 'sign_up#index'
2. コントローラーの実装
app/controllers/sign_up_controller.rb
class SignUpController < ApplicationController def index; end def new @user = User.new end def create @user = User.new(user_params) if @user.save redirect_to sign_up_thanks_url, status: :see_other else render :new, status: :unprocessable_entity end end private def user_params params.fetch(:user, {}).permit(:email, :password) end end
3. viewsの実装
app/views/sign_up/new.html.erb
<%= form_with model: @user, url: sign_up_path, local: true do |f| %> <div> <label>メールアドレス</label> <%= f.email_field :email, placeholder: "user@example.com", required: true %> </div> <div> <label>パスワード</label> <%= f.password_field :password, placeholder: "英数字を2つ以上組み合わせよう", required: true %> </div> <div> <%= f.submit "会員登録する" %> </div> <% end %>
app/views/sign_up/index.html.erb
<div>会員登録ありがとうございます。</div>
ログインの実装
1. ルーティングを設定する
config/routes.rb
root 'top#index' get '/sign_in', to: 'session#new' post '/sign_in', to: 'session#create'
2. ヘルパーの実装
app/helper/session_helper.rb
module SessionHelper def sign_in(user) session[:user_id] = user.id end def current_user if session[:user_id].present? @current_user ||= User.find_by(id: session[:user_id]) end end def signed_in? current_user.present? end def sign_out session.delete(:user_id) @current_user = nil end end
3. コントローラーの実装
app/controllers/application_controller.rb
class ApplicationController < ActionController::Base include SessionHelper before_action :signed_in_user private def signed_in_user unless signed_in? redirect_to sign_in_url end end end
app/controllers/session_controller.rb
class SessionController < ApplicationController skip_before_action :signed_in_user def new @user = User.new end def create @user = User.find_by(email: user_params[:email].downcase) if @user && @user.authenticate(user_params[:password]) sign_in @user redirect_to root_url else @user = User.new(email: user_params[:email].downcase) render 'new', status: :unprocessable_entity end end def destroy sign_out if signed_in? redirect_to root_url, status: :see_other end private def user_params params.fetch(:user, {}).permit(:email, :password) end end
app/controllers/top_controller.rb
class TopController < ApplicationController def index; end end
app/controllers/sign_up_controller.rb
以下を追記する
skip_before_action :signed_in_user
4. viewsの実装
app/views/session/new.html.erb
<%= form_with model: @user, url: sign_in_path, local: true do |f| %> <div> <label>メールアドレス</label> <%= f.text_field :email, placeholder: "user@example.com", required: true %> </div> <div> <label>パスワード</label> <%= f.password_field :password, placeholder: "英数字を2つ以上組み合わせよう", required: true %> </div> <div> <%= f.submit "ログイン" %> </div> <% end %>
app/views/top/index.html.erb
<div>ログインしました!</div>
ログアウトの実装
1. ルーティングを設定する
config/routes.rb
delete '/sign_out', to: 'session#destroy'
2. コントローラーの修正
app/controllers/session_controller.rb
def destroy sign_out if signed_in? redirect_to root_url, status: :see_other end
3. viewsの実装
app/views/top/index.html.erb
ログアウトボタンを追加する
<%= link_to "ログアウト", sign_out_path, data: { 'turbo-method': :delete } %>
終わり
以上で実装は終わりです!
必要に合わせてバリデーションや、カラムを足してみてくださいね。
メールアドレスを使った認証や、パスワードリセットなどは別記事で紹介したいと思います。