[Ruby on Rails]sorceryによる認証 – (1)メールアドレスとパスワードによる認証のチュートリアル

rails

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

Railsで認証を行う時に使えるGemは幾つもあるかと思います。今回はこれらの中でも比較的シンプルだと言われているsorceryのチュートリアルをやってみました。

sorceryの特徴

sorceryの特徴について、公式サイトから幾つか抜粋します。

・ Less is more - less than 20 public methods to remember for the entire feature-set make the lib easy to 'get'.
・No built-in or generated code - use the library's methods inside your own MVC structures, and don't fight to fix someone else's.
・Magic yes, Voodoo no - the lib should be easy to hack for most developers.

sorcry公式ページより

20個のメソッドが用意されており、それらをRailsのMVCの中で使うだけでよく、黒魔術を否定している、といったところでしょうか。。。

チュートリアル

では、チュートリアルです。タイトルにも書きましたがメールアドレスとパスワードでの認証を行うサンプルを作ります。ほぼ公式サイトのチュートリアルの手順通りでしたが、幾つか追加した手順もあり、それについても書いていこうと思います。

1.プロジェクトの作成からsorceryの準備

rails new などでプロジェクトを作成したら、Gemfileにsorceryを追記してbundle installを行います。

Gemfile
gem 'sorcery'

プロジェクトにsorceryをインストールするため、以下のコマンドを実行します。

$ rails g sorcery:install

2.Userモデルとその周りの作成

次にUserモデルと、その周りのビュー・コントローラを作成します。最初にrake db:migrateを行うと、sorceryによるUserモデルの雛形ができるようです。

$ rake db:migrate

スキャフォールドでビュー・コントローラを作成します。

$ rails g scaffold user email:string crypted_password:string salt:string --migration false

crypted_password、saltを編集・表示させないようにするため、app/views/users配下のファイルから、これらの記述を削除します(今回は_form.html.erb、index.html.erb、show.html.erbから削除しました)。

フォームよりメールアドレス・パスワードを受け取るため、コントローラに以下を追加します。

app/controllers/users_controller.rb
private

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

フォームにパスワードを入力する箇所を追加します。

app/views/users/_form.html.erb
<div class="field">
   <%= f.label :password %><br />
   <%= f.password_field :password %>
</div>
<div class="field">
   <%= f.label :password_confirmation %><br />
   <%= f.password_field :password_confirmation %>
</div>

Userモデルにsorceryを使うことの宣言、及びバリデーションを追加します。

app/models/user.rb
class User < ActiveRecord::Base
  authenticates_with_sorcery!

  validates :password, length: { minimum: 3 }
  validates :password, confirmation: true
  validates :password_confirmation, presence: true

  validates :email, uniqueness: true
end

暗号化されたパスワード、saltは表示したくないため、app/views/users/index.html.erbより以下の記述を消去します。

app/views/users/index.html.erb
        <td><%= user.crypted_password %></td>
        <td><%= user.salt %></td>

3.ログイン周りの作成

作成したUserモデルでログイン、ログアウトするUserSessionコントローラを作成します。次のコマンドを実行してください。

$ rails g controller UserSessions new create destroy

作成されたコントローラを以下のように編集します。

app/controllers/user_sessions_controller.rb
class UserSessionsController < ApplicationController
  def new
    @user = User.new
  end

  def create
    if @user = login(params[:email], params[:password])
      redirect_back_or_to(:users, notice: 'Login successful')
    else
      flash.now[:alert] = 'Login failed'
      render action: 'new'
    end
  end

  def destroy
    logout
    redirect_to(:users, notice: 'Logged out!')
  end
end

createアクション、destroyアクションでそれぞれsorceryに用意されている「login」「logout」を呼び出しています。

ログインするためのフォームを作成します。

app/views/user_sessions/new.html.erb
<h1>Login</h1>

<%= render 'form' %>

<%= link_to 'Back', user_sessions_path %>
app/views/user_sessions/_form.html.erb
<%= form_tag user_sessions_path, :method => :post do %>
  <div class="field">
    <%= label_tag :email %><br />
    <%= text_field_tag :email %>
  </div>
  <div class="field">
    <%= label_tag :password %><br />
    <%= password_field_tag :password %>
  </div>
  <div class="actions">
    <%= submit_tag "Login" %>
  </div>
<% end %>

ルーティングを定義します。

config/routes.rb
root :to => 'users#index'
resources :user_sessions
resources :users

get 'login' => 'user_sessions#new', :as => :login
post 'logout' => 'user_sessions#destroy', :as => :logout

ログイン・ログアウトをするリンクと、メッセージを通知する領域をapplication.html.erbのbodyタグ内に追加します。

app/views/layouts/application.html.erb
(中略)
<body>

  <div id="nav">
    <% if current_user %>
      <%= link_to "Edit Profile", edit_user_path(current_user.id) %>
      <%= link_to "Logout", :logout, method: :post %>
    <% else %>
      <%= link_to "Register", new_user_path %> |
      <%= link_to "Login", :login %>
    <% end %>
  </div>
  <div>
    <p id="notice"><%= flash[:notice] %></p>
    <p id="alert"><%= flash[:alert] %></p>
  </div>
  <%= yield %>

</body>
(中略)

ユーザを登録した時の遷移先を変えます。

app/controllers/users_controller.rb
(中略)
if @user.save
  redirect_to(:users, notice: 'User was successfully created')
(中略)

4.ログインしていないユーザの操作を制限する

ログインしていないユーザに、登録済みのユーザの削除などをされたら困ります。なのでコントローラにログインしていないユーザのの操作を制限するよう定義します。

app/controllers/application_controller.rb
before_filter :require_login

(中略)
  
private
  
  def not_authenticated
    redirect_to login_path, alert: "Please login first"
  end
app/controllers/users_controller.rb
skip_before_filter :require_login, only: [:index, :new, :create]
app/controllers/user_sessions_controller.rb
skip_before_filter :require_login, except: [:destroy]

application_controllerに「before_filter」を定義し、デフォルトではログインを必須(require_login)としています。そしてusers_controller、user_sessions_controllerにログイン必須を適用しないアクションを定義しています。ログインしていない場合、sorceryによりapplication_controllerの「not_authenticated」が呼び出されます。

5.独自に追加した手順

ここまでsorceryの公式チュートリアルをなぞってきました。これらの他に私は以下の手順を追加しました。

ログイン画面の「Back」リンクの遷移先をユーザ一覧にするため以下のようにします。

app/views/user_sessions/new.html.erb
<%= link_to 'Back', users_path %>

先の手順でapplication.html.erbのメッセージ通知領域(notice)を定義したので、ダブらないよう

  • app/views/users/index.html.erb
  • app/views/users/show.html.erb

から以下の記述を削除します。

<p id="notice"><%= notice %></p>	

以上で完成です。rails s でアプリを起動し、ユーザ作成・ログイン・ログアウト・ユーザ削除などをやってみてください。

まとめ

sorceryのチュートリアルをなぞってみただけですが、ごく自然にsorceryのメソッドを呼び出すことで認証機能を実現できることが分かりました。こららが画面を持たないAPIの作成や、ユーザ名での認証などにも適用できるのか等、いろいろ興味があります。何かのときの参考になれば幸いです。

参考サイト

sorcery
公式チュートリアル