Deviseの使い方(各種オプション編1)
devise gemはモジュールの概念に基づいて作成されています。 この性質のおかげで開発者は自分のアプリに本当に必要なものだけをピンポイントに導入することが可能です。使用していないコードをアプリに組み込まずに済むのは可読性の面からとてもありがたいですね。
※ここではマッピングされたモデルはUserであると仮定して話を進めていきます。
※また、Deviseはwardenという認証の仕組みをベースに採用していますが、今回Wardenについては深く触れません。
wardengemはこちら
- Deviseの使い方(各種オプション編1)
- 各種モジュール
- deviseをインストールすることで使用可能になる汎用メソッド
- Database Authenticatable
- required_fields(klass) ⇒ Object
- after_database_authentication ⇒ Object
- authenticatable_salt ⇒ Object
- clean_up_passwords ⇒ Object
- destroy_with_password(current_password) ⇒ Object
- password=(new_password) ⇒ Object
- password_digest(password)⇒ Object (protected)
- send_email_changed_notification ⇒ Object
- skip_email_changed_notification! ⇒ Object
- skip_email_changed_notification! ⇒ Object
- skip_password_change_notification! ⇒ Object
- update_with_password(params, *options) ⇒ Object
- update_without_password(params, *options) ⇒ Object
- valid_password?(password) ⇒ Boolean
- Registerable
各種モジュール
Deviseは大きく10のモジュールから構成されています。
- Database Authenticatable
- Registerable
- Validatable
- Recoverable
- Rememberable
- Confirmable
- Lockable
- Timeoutable
- Trackable
- Omniauthable
今回はhelpersモジュールに定義されている汎用メソッドと Database Authenticatable, Registerableの2つを利用した時に使えるメソッドを解説していきます。
deviseをインストールすることで使用可能になる汎用メソッド
deviseを実装するとhelpersモジュールに定義されているメソッドを使用することが可能になります。deviseを利用する場合はここに定義されているメソッドは必ずと言っていいほど使用するので、こちらを最初に解説します。
authenticate_user! user_signed_in? current_user user_session
authenticate_user!
文字通りユーザーの認証を行うメソッド。
before_action :authenticate_user!
というように、あるリソースにアクセスできるかどうかを認証によって判定する時に使用されることが多い。
deviseのソースは下記
def authenticate_#{mapping}!(opts={})
opts[:scope] = :#{mapping}
warden.authenticate!(opts) if !devise_controller? || opts.delete(:force)
end
認証が走るのはdevise_controller 判定がfalseの時か、オプションでforceキーが与えられている時に認証が行われる。
current_user
多分deviseを使う上で一番お世話になるメソッド。 ログイン済みのユーザー情報を取得できるので大体これ使っとけば良い。
def current_#{mapping}
@current_#{mapping} ||= warden.authenticate(scope: :#{mapping})
end
mappingというのはここではuserが入る.なので@current_#{mapping}は今回は@current_userになると読み替えてください。
やっていることとしては、@current_userがtrueであれば@current_userを返却し、なければwarden.authenticate(:user)として認証を行いその値を@current_userに入れる。所謂nilガード構文。
使い方としてはログインしたユーザーのインスタンスがこのメソッドで返却されるので、
current_user.email #=> 今ログインしているユーザーのメールアドレス
このような感じで使用できる。
user_signed_in?
Userがログインしているかどうかの判定に用いられることの多いメソッド。 実態は 上記のcurrent_userメソッドを二重否定している
!!current_user
ので、current_user判定でも同様のことが実行できる。コード内にあれば明示的にログインしているかどうか判断したいと一目でわかるので意思表示にもおすすめ。
user_session
current_userとwarden.session(:user)を判定してhash型の値のリストを返します。timeoutableモジュールとか使う時にお世話になるかも。
{‘last_logged_in_at’ => 1586134613482}
のような感じでUTCの時刻が入ってたりします。(例)
def #{mapping}_session
current_#{mapping} && warden.session(:#{mapping})
end
Database Authenticatable
password をハッシュ化し、ログイン中のユーザーの信頼性を検証するモジュール。デフォルトで有効になっている。
クラスメソッド
required_fields(klass) ⇒ Object
該当モデルに対して何がauthenticate上必須なのかを出力するメソッド。
デフォルトでは:encrypted_password, :email]が出力される。
固定で:encrypt_passwordが設定されており、それに加えてKlass.authentication_keysが追加された結果が出力される。
これは各モジュールに同様のクラスメソッドが定義されている。また、各モジュールには同名のクラスメソッドが用意されており、各モジュールで必要なものがあればオーバーライドされて使用される。
インスタンスメソッド
after_database_authentication ⇒ Object
インスタンスが認証に成功した後に呼ばれるコールバックメソッド。 ユーザーが認証に成功した後になんらかの処理を行いたい場合に特定の処理を行いたい時にこのメソッドの中にロジックを定義する。
def after_database_authentication self.update_attribute(:invite_code, nil) end
authenticatable_salt ⇒ Object
実装方法を問わずソルト値を取得できる。 ソルト値で比較検討したい時とかに使える
def authenticatable_salt encrypted_password[0,29] if encrypted_password end
実行結果はこんな感じ。
current_user.authenticatable_salt #=> "$2a$11$oUYu5DBb2G9NTQ5bG9q4lu"
clean_up_passwords ⇒ Object
passwordとpassword_confirmationの値をnilにする。通常では新規登録に失敗した時に呼ばれる。登録失敗時にpasswordが空欄になるのはこのメソッドが呼ばれるから。
def clean_up_passwords self.password = self.password_confirmation = nil end
destroy_with_password(current_password) ⇒ Object
現在のパスワードを引数にとり、それがマッチしている場合はレコードを削除する。戻り値はbool型。
def destroy_with_password(current_password)
result = if valid_password?(current_password)
destroy
else
valid?
errors.add(:current_password, current_password.blank? ? :blank : :invalid)
false
end
result
end
password=(new_password) ⇒ Object
与えられた値からハッシュされたパスワードを生成する。ハッシュ化されたパスワードはencrypted_passwordカラムに保存される。
def password=(new_password) @password = new_password self.encrypted_password = password_digest(@password) if @password.present? end
password_digest(password)⇒ Object (protected)
bycrytを使用してパスワードをハッシュ化する。もし独自のアルゴリズムを使用したい場合はこのメソッドをオーバーライドする。password=(new_password)に使用されているprotectedのメソッド。
def password_digest(password) Devise::Encryptor.digest(self.class, password) end
send_email_changed_notification ⇒ Object
メールアドレス変更の通知を送る。通知の送り先は変更前のメールアドレス。 View on GitHub
def send_email_changed_notification send_devise_notification(:email_changed, to: email_before_last_save) end
skip_email_changed_notification! ⇒ Object
send_email_changed_notification のpassword版。登録されているemailアドレスにパスワードが変更された旨の通知を送る。
View on GitHub
def send_password_change_notification send_devise_notification(:password_change) end
skip_email_changed_notification! ⇒ Object
emailのカラム更新後に通知を送らないようにする。 View on GitHub
def skip_email_changed_notification! @skip_email_changed_notification = true end
skip_password_change_notification! ⇒ Object
passwordのカラム更新後に通知を送らないようにする。 View on GitHub
def skip_password_change_notification! @skip_password_change_notification = true end
update_with_password(params, *options) ⇒ Object
入力されたパスワードがマッチしていれば(current_password属性)レコードを更新する。そうでない場合はfalseが返る。 パスワードを変更する場合など、現在のパスワード・新しいパスワード・確認用の新しいパスワード を入れる時などに呼ばれるメソッド。現在のパスワードを要求したいときの処理に使われる。 このメソッドではcurrent_passwordを入力することで例えばnameカラムなどを変更することができる。passwordフィールドとpassword_confirmカラムがformに存在する場合、片方だけの記述を行うとバリデーションに引っかかって更新できない。また、両方にcurrent_password以外の有効な値を入れる(つまり、passwordの更新を行う)場合、ログアウト処理が走るので、注意。current_password, password, password_confirmationに同じ値が入っている場合、更新処理が走り、ログアウトもされない。
def update_with_password(params, *options)
if options.present?
ActiveSupport::Deprecation.warn <<-DEPRECATION.strip_heredoc
[Devise] The second argument of `DatabaseAuthenticatable#update_with_password`
(`options`) is deprecated and it will be removed in the next major version.
It was added to support a feature deprecated in Rails 4, so you can safely remove it
from your code.
DEPRECATION
end
current_password = params.delete(:current_password)
if params[:password].blank?
params.delete(:password)
params.delete(:password_confirmation) if params[:password_confirmation].blank?
end
result = if valid_password?(current_password)
update(params, *options)
else
assign_attributes(params, *options)
valid?
errors.add(:current_password, current_password.blank? ? :blank : :invalid)
false
end
clean_up_passwords
result
end
update_without_password(params, *options) ⇒ Object
パスワードの要求なしでカラムを変更する。上記の逆版。パスワードがないと変更したくない値がある場合、このメソッドをオーバーライドして保護する必要がある。
def update_without_password(params, *options)
if options.present?
ActiveSupport::Deprecation.warn <<-DEPRECATION.strip_heredoc
[Devise] The second argument of `DatabaseAuthenticatable#update_without_password`
(`options`) is deprecated and it will be removed in the next major version.
It was added to support a feature deprecated in Rails 4, so you can safely remove it
from your code.
DEPRECATION
end
params.delete(:password)
params.delete(:password_confirmation)
result = update(params, *options)
clean_up_passwords
result
end
valid_password?(password) ⇒ Boolean
引数にpasswordを渡し、そのパスワードがユーザーパスワードであるかどうかを確認する。戻り値はboolean View on GitHub
def valid_password?(password) Devise::Encryptor.compare(self.class, encrypted_password, password) end
Registerable
新しいリソースの登録に関する全ての責任を負う。 なんとこいつが提供してくれるインスタンスメソッドはない。 OAuthなどで利用される。
model定義はこちら。
# frozen_string_literal: true
module Devise
module Models
# Registerable is responsible for everything related to registering a new
# resource (ie user sign up).
module Registerable
extend ActiveSupport::Concern
def self.required_fields(klass)
[]
end
module ClassMethods
# A convenience method that receives both parameters and session to
# initialize a user. This can be used by OAuth, for example, to send
# in the user token and be stored on initialization.
#
# By default discards all information sent by the session by calling
# new with params.
def new_with_session(params, session)
new(params)
end
end
end
end
end
こちらの記事では以上です。他のモジュールについても、こちらの記事で紹介しています。