何をしたか
sorcery
というgemを使って、ログイン機能を実装しました。初めて使うgemで、日本語で見られるドキュメントも少なかったので、自分のためにも手順書とマニュアルを作っておこうと思います。
なお、実行環境は下記の通りです。
Rails 5.2.3
Ruby 2.6.4
どんなgem?
認証のための最小限のロジックを積んだgemで、ユーザーの必要に応じてメソッドを増やしたり拡張をしていくことを目的としています。
▼公式ドキュメントはこちら
Sorcery/sorcery
導入方法
この記事に記す手順は、概ね公式のチュートリアルに沿っています。Gemの導入から、ログイン・ログアウト機能の実装までを記載しています。
以下、手順です。
まずは以下をGemfileに記載し...
gem 'sorcery'
bundle install
します。
続けて、以下のコマンドを実行すると
$ rails generate sorcery:install
以下のファイルが生成されます。
create app/models/user.rb
create db/migrate/XXXXXXXXX_sorcery_core.rb
また、生成されたマイグレーションファイルの中身はこちらです。
class SorceryCore < ActiveRecord::Migration[5.2]
def change
create_table :users do |t|
t.string :email, null: false
t.string :crypted_password
t.string :salt
t.timestamps null: false
end
add_index :users, :email, unique: true
end
end
crypted_password
(暗号化されたパスワード)はまだわかるとして、salt
カラムが謎ですよね。WEB上にこんな記事を発見しまして、以下、そのGoogle翻訳です。
標準の暗号化では、「ソルト」を使用して、パスワードハッシュの安全性を高めます。 Sorceryでは、パスワードの末尾にランダムな文字列を結合し、その文字列をソルトフィールドに記憶することでこれを行います。
なるほど。。。rails db:migrate
をして、無事、導入完了です。
実装(1):ユーザーの作成
View
crypted_password
や、salt
カラムのデータはユーザーが直接変更・表示できないようにするため、ビューには書きません。その代わり、ビューではpassword
とpassword_confirmation
を利用します。
= form_with model: @user, local: true do |f|
= f.label :email
= f.text_field :email
= f.label :password
= f.password_field :password
= f.label :password_confirmation
= f.password_field :password_confirmation
= f.submit "登録"
Controller
また、controllerのstrong_paramater
では、password
とpassword_confirmation
をそれぞれ受け取れるようにします。
class UsersController < ApplicationController
# ...
private
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
end
Model
モデルファイルは以下のように記述しました。
class User < ActiveRecord::Base
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
まず、authenticates_with_sorcery!
で、Uderモデルにsorcery
による認証機能を持たせます。
password
やpassword_confirmation
のようなモデルにないカラムについては、
validates :password, confirmation: true, if: -> { new_record? || changes[:crypted_password] }
こちらの記述で、sorcery
に、それがcrypted_password
カラムの内容であることを認識させます。
余談
余談ですが、new_redord?
メソッドは、インスタンスが新規に作成されるものかどうかを判定するActive Recordのメソッドのようですね...(知らなかった&&便利!)
また、if: ->
以下は条件付きバリデーションの、シンボルやLambdasてやProcの部分ですね。苦手な部分なので、頑張って復習しないと。。。
コンソールで試す
ここまでできたら、rails c
をし、ユーザーが無事作成できることを確かめてみます。
# コンソール
> User.create!(email: "test@email.com", password: "test12345", password_confirmation: "test12345")
> User.last
=> #<User:0x00007fb37b8b4d28
id: 1,
email: "test@email.com",
crypted_password:
"$2a$10$dOdyPmn65bJ4LKjG5vdL2eU2Hypzpqz5fEgp4LTF8ai0lJtBKL.Q6",
salt: "QapQbW9crUf1QixW2BD7",
username: "testuser",
created_at: Tue, 22 Dec 2020 14:48:37 UTC +00:00,
updated_at: Tue, 22 Dec 2020 14:48:37 UTC +00:00>
無事、crypted_password
とsalt
を持つユーザーが作成されました
追記
そういえば、Usersコントローラーのメソッドを記載していなかったと気がついたので、users_controllerの全体像を記しておきます。
class UsersController < ApplicationController
def new
@user = User.new
end
def create
@user = User.new(user_params)
if @user.save
redirect_to login_path
flash[:notice] = 'ユーザーの作成に成功しました'
else
flash.now[:alert] = 'ユーザーの作成に失敗しました'
render :new
end
end
private
def user_params
params.require(:user).permit(:username, :email, :password, :password_confirmation)
end
end
これで、画面からもユーザーを作成できるようになりました^^
実装(2):ログイン/ログアウト機能の作成
それでは、ログイン・ログアウト機能を実装していきたいと思います。sorceryで提供しているメソッドについては、公式のこちらの部分に記述があります。
それによると、login
メソッドは、email
、password
、remenber_me
(デフォルト値はfalse)の3つの引数を取ることができると分かります。
login(email, password, remember_me = false)
今回は、remenber_me
機能は利用していないので、email
とpassword
でログイン機能を実装します。まずはターミナルからコントローラーを生成し...
$ rails g controller UserSessions new create destroy
コントローラーを記載していきます。
class UserSessionsController < ApplicationController
def new
end
def create
@user = login(params[:email], params[:password])
if @user
redirect_back_or_to(new_user_path, notice: 'ログインに成功しました')
else
flash.now[:alert] = 'ログインに失敗しました'
render :new
end
end
def destroy
logout
redirect_to(login_path, notice: 'ログアウトしました')
end
end
redirect_back_or_to
はsorceryでフレンドリーフォーワーディングを実現する機能だそうで、こちらの記述によると、(url, flash_hash = {})
を引数にとるそうです。
変数名 = {}
は引数としてハッシュをまるっと受け取るときの書き方。チェリー本のp.165に書いてありました。
logout
メソッドは説明は不要ですね。
Router
ルーティングは以下のように定義しました。
Rails.application.routes.draw do
get 'login', to: 'user_sessions#new' # 追記
post 'login', to: 'user_sessions#create' # 追記
delete 'logout', to: 'user_sessions#destroy' # 追記
root 'users#new'
resources :users, only: %i(new create)
end
ここも、特に説明はいらないと思います。
View
最後に、Viewは以下のように定義しました。ここも、特に気遣いが必要だったことはないです。
body
= render 'shared/header'
= yield
下記のlogged_in?
は**sorcery
のメソッドで、ビューで使えるもの**であると公式のドキュメントには書いてありました。
h1 AppName
nav
- if logged_in?
= link_to 'ログアウト', logout_path, method: :delete
- else
= link_to 'ログイン', login_path
= form_with url: login_path, method: :post do |f|
= f.label :email
= f.text_field :email
= f.label :password
= f.password_field :password
= f.submit 'ログイン'
完成!
以上で、ログイン・ログアウトメソッドが実装できました。
見た目等はこれから整えていきますが、想像以上に簡単でした^^
感想としては、初めて作ったこともあって、特にGem等を使わずに、Railsのデフォルトの機能を使ってシンプルなログインフォームを作ったぐらいの手間な気がしましたが、
deviseの時みたいに実装にクセがある感じもなかったですし、sorcery
で実現できるメソッド一覧にはSNS認証などもあるようで、これからも色々試していきたいです^^