Railsの認証機能実装gemといえばdeviseが有名だと思う。deviseを使うと実装そのものはとても簡単に実現できるのだが、内部構造が複雑なため、漫然と使うのは少し不安。そこで、別の認証gemであるsorceryを使用してみた。
基本的には公式のtutorial通りだけど所々違います。
環境
ruby 2.0
rails 4.2
テンプレートエンジン slim
準備
まずは以下のgemを追加してbundle installする。
gem 'sorcery'
必要な機能をインストールするために、以下のコマンドを実行。
rails g sorcery:install
rake db:migrate
これで登録ユーザーを保存するためのUserモデルが作成される。ちなみに"User"というモデル名はオプションで変更することもできる。
ユーザー登録機能の実装
Userモデルのコントローラーとビューを作成する。今回はジェネレーターを使うので、以下のコマンドを実行。
rails g scaffold user email:string crypted_password:string salt:string --migration false
"users_controller.rb"の以下の部位を編集
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
user_paramsメソッドはcreate、updateアクションから呼ばれる。フォームに入力した内容をstrong Parameterに関する処理を行った上で、上記のメソッドに渡す。
次にビューの設定を行う。メールアドレスとパスワードを入力するフォームと登録ボタンを配置する。
=form_for(@user) do |f|
-if @user.errors.any?
div#error_explanation
h2 =pluralize(@user.errors.count, "error") + 'prohibited this user from being saved:'
ul
-@user.errors.full_messages.each do |message|
li =message
div.field
=f.label :email
br
=f.text_field :email
div.field
=f.label :password
br
=f.text_field :password
div.field
=f.label :password_confirmation
br
=f.text_field :password_confirmation
div.actions
=f.submit
これで取りあえずユーザー登録は出来るようになるはず。しかし、このままだと、ユーザー一覧画面(index.slim)や詳細画面(show.slim)に暗号化されたパスワードが表示されてしまうので、これらのビューに少しだけ手を加える。
h1 Listing Users
table
thead
tr
th Email
th colspan="3"
tbody
-@users.each do |user|
tr
td =user.email
td =link_to 'Show', user
td =link_to 'Edit', edit_user_path(user)
td =link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' }
br
= link_to 'New User', new_user_path
p
strong Email:
=@user.email
=link_to 'Edit', edit_user_path(@user)
span |
=link_to 'Back', users_path
最後に、モデルにバリデーションを行う
class User < ActiveRecord::Base
authenticates_with_sorcery!
validates :password, length: { minimum: 3 }, if: -> { new_record? || changes["password"] }
validates :password, confirmation: true, if: -> { new_record? || changes["password"] }
validates :password_confirmation, presence: true, if: -> { new_record? || changes["password"] }
validates :email, uniqueness: true
end
これでユーザー登録、メールアドレスの変更、パスワードの変更、ユーザーの削除が行えるようになった。
ログイン、ログアウト機能の実装
次に登録したユーザーのログイン、ログアウト状態を管理するためのコントローラーを作成する。以下のコマンドを実行。
rails g controller UserSessions new create destroy
生成されたコントローラーを以下のように編集。
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アクション中の"login"とdestroyアクションの"logout"はsorceryによって提供される主要メソッド。
コントローラーに対応するビューを作成。
h1 Login
==render 'form'
=link_to 'Back', users_path
=form_tag user_sessions_path, method: :post do
div.field
=label_tag :email
=text_field_tag :email
div.field
=label_tag :password
=password_field_tag :password
div.field
=submit_tag 'Login'
ルーティングの設定
root 'users#index'
resources :user_sessions
resources :users
get 'login' => 'user_sessions#new', :as => :login
post 'logout' => 'user_sessions#destroy', :as => :logout
ログイン及びログアウトボタンや通知を表示するための領域を作成。今回はヘッダー領域の部分テンプレートとして作成する。
html
head
title Marktodo
=stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true
= javascript_include_tag 'application', 'data-turbolinks-track' => true
= csrf_meta_tags
body
==render 'shared/header'
==yield
div#nav
-if current_user
=link_to "Edit Profile", edit_user_path(current_user.id)
span |
=link_to "Logout", :logout, method: :post
-else
=link_to "Register", new_user_path
span |
= link_to "Login", :login
div
p#notice =flash[:notice]
p#alert =flash[:alert]
最後に、アクセス制限を実現するために"application_controller.rb"に以下を追加
class ApplicationController < ActionController::Base
before_filter :require_login
中略~
これだけだと非ログインユーザーは全てのページにアクセスできなくなるので、トップ画面(index)や登録、ログインに関わるアクションにはアクセスできるようにする。
class UsersController < ApplicationController
skip_before_action :require_login, only: [:index, :new, :create]
中略~
class UserSessionsController < ApplicationController
skip_before_action :require_login, except: [:destroy]
中略~
これで登録・認証に必要な一通りの処理が行えるようになる。