Railsの認証管理gem sorcery導入メモ(slim)


Railsの認証機能実装gemといえばdeviseが有名だと思う。deviseを使うと実装そのものはとても簡単に実現できるのだが、内部構造が複雑なため、漫然と使うのは少し不安。そこで、別の認証gemであるsorceryを使用してみた。

基本的には公式のtutorial通りだけど所々違います。

環境
ruby 2.0
rails 4.2
テンプレートエンジン slim

## 準備
まずは以下のgemを追加してbundle installする。

```rb:gemfile
gem 'sorcery'
```

必要な機能をインストールするために、以下のコマンドを実行。

```bash
rails g sorcery:install
rake db:migrate
```

これで登録ユーザーを保存するためのUserモデルが作成される。ちなみに"User"というモデル名はオプションで変更することもできる。

## ユーザー登録機能の実装
Userモデルのコントローラーとビューを作成する。今回はジェネレーターを使うので、以下のコマンドを実行。

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

"users_controller.rb"の以下の部位を編集

```rb:app/controllers/users_controller.rb
def user_params
      params.require(:user).permit(:email, :password, :password_confirmation)
end
```

user_paramsメソッドはcreate、updateアクションから呼ばれる。フォームに入力した内容をstrong Parameterに関する処理を行った上で、上記のメソッドに渡す。

次にビューの設定を行う。メールアドレスとパスワードを入力するフォームと登録ボタンを配置する。

```haml:views/users/_form.slim
=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)に暗号化されたパスワードが表示されてしまうので、これらのビューに少しだけ手を加える。

```haml:view/users/index.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

```

```haml:view/users/show.slim
p
  strong Email:
  =@user.email


=link_to 'Edit', edit_user_path(@user)
span |
=link_to 'Back', users_path
```

最後に、モデルにバリデーションを行う

```rb:models/user.rb
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
```

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

```rb: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アクション中の"login"とdestroyアクションの"logout"はsorceryによって提供される主要メソッド。

コントローラーに対応するビューを作成。

```haml:app/views/user_sessions/new.slim
h1 Login

==render 'form'

=link_to 'Back', users_path
```

```haml:app/views/user_sessions/_form.slim
=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'
```

ルーティングの設定

```rb:config/routes.rb
root  'users#index'
resources :user_sessions
resources :users

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

ログイン及びログアウトボタンや通知を表示するための領域を作成。今回はヘッダー領域の部分テンプレートとして作成する。

```haml:app/views/layout/application.slim
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
```

```haml:app/views/shared/_header.slim
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"に以下を追加

```rb:app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
	before_filter :require_login
    中略~
```

これだけだと非ログインユーザーは全てのページにアクセスできなくなるので、トップ画面(index)や登録、ログインに関わるアクションにはアクセスできるようにする。

```rb:app/controllers/users_controller.rb
class UsersController < ApplicationController
  skip_before_action :require_login, only: [:index, :new, :create]
    中略~
```

```rb:app/controllers/users_sessions_controller.rb
class UserSessionsController < ApplicationController
	skip_before_action :require_login, except: [:destroy]
    中略~
```

これで登録・認証に必要な一通りの処理が行えるようになる。