Passion make things more better

Ruby on Rails / React.js / Swift / AWS / Docker

sorceryを使ってEmail + Passwordログインを実装

Sorceyを用いたログイン機能実装を解説します。

Railsのログイン機能でよく知らているdeviseよりも機能が少なく、簡単に扱えるのでこちらを採用しております。

実装の詳細に関してはSorceryのGitHubリポジトリwikiに書かれています。 今回の実装はwikiSimple Password Authenticationを参考にしていきます。

Gemの導入

以下をGemfileに追加し、bundle installを実行してください。

gem 'sorcery'

必要ファイルの生成

sorceryにはgenerateコマンドが備わっています。以下のコマンドを実行すると、必要ファイルが生成されます。

bundle exec rails g sorcery:install
      create  config/initializers/sorcery.rb
    generate  model User --skip-migration
      invoke  active_record
      create    app/models/user.rb
      invoke    test_unit
      create      test/models/user_test.rb
      create      test/fixtures/users.yml
      insert  app/models/user.rb
      insert  app/models/user.rb

migrationファイルの作成

先程のコマンドだと、migrationファイルが生成されていません。以下のコマンドを実行し、migrationファイルを作成しましょう。

※何か問われた場合は、すべて「Y」と入力し進めてください。

$ bundle exec rails g model User name:string email:string

生成されたmigrationファイルを以下のように編集してください。編集が終わったら、bundle exec rake db:migrateを実行し適用させましょう。

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

      t.timestamps null: false
    end
  end
end

User modelの編集

User modelに対して、sorceryを使用するための設定およびValidationを追加します。app/models/user.rbを以下のように編集してください。

class User < ApplicationRecord
  authenticates_with_sorcery!

  validates :password, length: { minimum: 3 }, if: -> { new_record? || changes[:crypted_password] }
  validates :password, confirmation: true, if: -> { new_record? || changes[:crypted_password] }
  validates :password_confirmation, presence: true, if: -> { new_record? || changes[:crypted_password] }

  validates :email, uniqueness: true
end

Routingの設定

ログイン周りのsession管理を扱うControllerをSessionsController、ユーザーの登録等を行うControllerをUsersControllerとし、以下のようにRouting(config/routes.rb)を編集します。

Rails.application.routes.draw do
  root to: 'pages#home'

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

  resources :users, only: [:new, :create]
end

Controllerの作成

Sorceryの実装に関わる、以下の4つのControllerを実装します。

  • ApplicationController
    • ベースの設定に関する処理
  • SessionsController
    • セッションの操作に関する処理
  • UsersController
    • ユーザーの操作に関する処理
  • PagesController
    • リダイレクト先として仮で用意(こちらは適宜Routingとともに自分で変更して問題ありません)

ApplicationController

ApplicationControllerにSorceryを使用するための設定を記述します。以下のようにapp/controllers/application_controller.rbを編集してください。

class ApplicationController < ActionController::Base
  # 全ページでログインを必須とする
  before_action :require_login

  private
  # ログインをしていない場合の処理
  def not_authenticated
    redirect_to login_path, alert: 'ログインしてください。'
  end
end

SessionsController

ログイン・ログアウト周りの処理を行う、SessionsControllerを作成します。以下の内容にてapp/controllers/sessions_controller.rbを作成してください。

※以下のようにskip_before_action :require_loginを指定することによって、ログインを必須ではなくすることができます。

class SessionsController < ApplicationController
  skip_before_action :require_login, only: [:new, :create]

  def new
  end

  def create
    user = login(params[:email], params[:password])
    if user.present?
      redirect_to root_path, notice: 'ログインしました。'
    else
      redirect_to login_path, alert: 'ログインに失敗しました。'
    end
  end

  def destroy
    logout
    redirect_to login_path, notice: 'ログアウトしました。'
  end
end

UsersController

ユーザー登録を行うためのUsersControllerを作成します。以下の内容にてapp/controllers/users_controller.rbを作成してください。

class UsersController < ApplicationController
  skip_before_action :require_login

  def new
    @user = User.new
  end

  def create
    @user = User.new(user_params)
    if @user.save
      login(params[:user][:email], params[:user][:password])
      redirect_to root_path, notice: 'ユーザー登録しました。'
    else
      render :new
    end
  end

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

PagesController

リダイレクト先の仮ページとしてPagesControllerを用意します。以下の内容にてapp/controllers/pages_controller.rbを作成してください。

class PagesController < ApplicationController
  skip_before_action :require_login

  def home
  end
end

Viewの作成

ここまでで、Routing、Model、Controllerの作成が終わりました。最後に以下のViewファイルを作成します。

※Viewにbootstrap3.xを使用しているため、htmlのclassにはその要素が適用されております。

├── layouts
│   └── application.html.erb
├── pages
│   └── home.html.erb
├── sessions
│   └── new.html.erb
└── users
    └── new.html.erb

pages#home

ログイン・ログアウト及びユーザー登録ページへの導線を配置します。

<div class="container">
  <h1>ホーム</h1>
  <ul>
    <li><%= link_to 'ログイン', login_path %></li>
    <li><%= link_to 'ユーザー新規登録', new_user_path %></li>
    <% if current_user %>
    <li><%= link_to 'ログアウト', logout_path, method: :post %></li>
    <% end %>
  </ul>
</div>

sessions#new

ログインページのViewとなります。

<div class="container">  
  <div class="row">
    <div class="col-md-4"></div>
    <div class="col-md-4">
      <%= form_tag login_path, method: :post do %>
      <div class="form-group">
        <label>メールアドレス</label>
        <%= text_field_tag 'email', nil, class: 'form-control' %>
      </div>
      <div class="form-group">
        <label>パスワード</label>
        <%= password_field_tag 'password', nil, class: 'form-control' %>
      </div>
      <div class="form-group text-center">
        <%= submit_tag 'ログインする', class: 'btn btn-primary' %>
      </div>
      <% end %>
    </div>
  </div>
</div>

users#new

ユーザー登録ページのViewとなります。

<div class="container">
  <div class="row">
    <div class="col-md-4"></div>
    <div class="col-md-4">
      <% if @user.errors.any? %>
      <div id="error_explanation">
        <h2><%= @user.errors.count %>件のエラーがあります。</h2>

        <ul>
          <% @user.errors.full_messages.each do |msg| %>
          <li><%= msg %></li>
          <% end %>
        </ul>
      </div>
      <% end %>

      <%= form_for @user, url: { action: 'create' } do |f| %>
        <div class="form-group">
          <%= f.label :name %>
          <%= f.text_field :name, class: 'form-control', required: true %>
        </div>
        <div class="form-group">
          <%= f.label :email %>
          <%= f.email_field :email, class: 'form-control', required: true %>
        </div>
        <div class="form-group">
          <%= f.label :password %>
          <%= f.password_field :password, class: 'form-control', required: true %>
        </div>
        <div class="form-group">
          <%= f.label :password_confirmation %>
          <%= f.password_field :password_confirmation, class: 'form-control', required: true %>
        </div>
        <div class="form-group text-center">
          <%= f.submit '登録する', class: 'btn btn-primary' %>
        </div>
      <% end %>
    </div>
  </div>
</div>

ここまで実装を行うと、ユーザー登録 -> ログイン -> ログアウトの一連の流れを確認することができます。