2011年12月21日水曜日

[Rails3] ログイン認証の要・不要をコントローラーによって使い分ける

実行環境:
ruby 1.9.3
Rails 3.1.3
ログイン認証を実現する際、認証が必要なコントローラー内に before_filter を書いてコントローラーを実行する前にログイン状態をチェックします。

たいてい、認証が必要なコントローラーが複数あります。それぞれに before_filter とそのメソッドを書くのはダサいので、before_filter を書いたコントローラークラスを作って継承させるのですが、認証させるコントローラーの数が多いと面倒になってきます。

すると「ええぃ、全てのコントローラーの親である ApplicationController に書いてしまえ!」と思うわけですが、逆に認証させたくないコントローラーが1つ2つあると、どうしてやろうか?と悩むことになります。

実現方法は色々あると思いますが、例としていくつかパターンを上げてみたいと思います。

ここでは例として
 ・CustomerController ⇒ 認証が必要(他にもいくつもある、という想定)
 ・LoginController ⇒ 認証が不要
という状況で説明してみようと思います。


① 認証用の before_filter を組み込んだコントローラーを作って継承させる(正攻法その1)
認証用の before_filter を組み込んだコントローラー(ここでは AuthController)を作り、認証が必要なコントローラーには AuthController を継承させます。

継承のイメージ
ApplicationController   ├─→ AuthController → CustomerController   └──────────→ LoginController
このような感じで継承させます。
以下がコードの大枠です。AuthController に認証の before_filter を仕込みます。
#{RAILS_ROOT}/app/controllers/auth_controller.rb
class AuthController < ApplicationController before_filter :login_required private def login_required # ログインしていなければログイン画面へリダイレクト end end
#{RAILS_ROOT}/app/controllers/customer_controller.rb
class CustomerController < AuthController end
#{RAILS_ROOT}/app/controllers/login_controller.rb
class LoginController < ApplicationController end


② 認証用のメソッドだけを ApplicationController で定義し、認証が必要なコントローラーだけで before_filter をセットする(正攻法その2)
before_filter にセットするプライベートメソッド(ここでは login_required)を ApplicationController で定義し、認証が必要なコントローラーには、個々のコードの中で before_filter をセットする。逆に認証不要なコントローラーにはセットしない。

継承のイメージ
ApplicationController   ├─→ CustomerController   └─→ LoginController
継承関係はデフォルトのままです。
以下がコードの大枠です。ApplicationController で認証用のプライベートメソッドのみを定義します。
#{RAILS_ROOT}/app/controllers/application_controller.rb
class ApplicationController < ActionController::Base # before_filter のセットは個々のコントロール内に書くのでここには書かない private def login_required # ログインしていなければログイン画面へリダイレクト end end
#{RAILS_ROOT}/app/controllers/customer_controller.rb
class CustomerController < ApplicationController before_filter :login_required # 認証プロセスを踏ませる end
#{RAILS_ROOT}/app/controllers/login_controller.rb
class LoginController < ApplicationController # 認証は不要なので、before_filter のセットはしない end


③ 認証用の before_filter を ApplicationController に組み込み、認証が不要なコントローラー内で before_filter をスキップする

継承のイメージ
ApplicationController   ├─→ CustomerController   └─→ LoginController
このような感じで継承させます。
以下がコードの大枠です。ApplicationController に認証の before_filter を仕込み、LoginController 内で before_filter の適用をキャンセルします。
#{RAILS_ROOT}/app/controllers/application_controller.rb
class ApplicationController < ActionController::Base before_filter :login_required private def login_required # ログインしていなければログイン画面へリダイレクト end end
#{RAILS_ROOT}/app/controllers/customer_controller.rb
class CustomerController < ApplicationController end
#{RAILS_ROOT}/app/controllers/login_controller.rb
class LoginController < ApplicationController skip_before_filter :login_required # before_filter を適用しない end

LoginController 内に skip_before_filter を書いていますが、認証不要なクラスが複数ある場合は、skip_before_filter をつけた NoauthController クラスを作って、LoginController に NoauthController クラスを継承させるといいでしょう。

④ 認証用の before_filter を適用する・しないの条件付きで ApplicationController に組み込む
上記の②と似たようなやり方ですが、before_filter の適用・非適用の条件を ApplicationController 内で設定します。
継承のイメージ
ApplicationController   ├─→ CustomerController   └─→ LoginController
②と同様、このような感じで継承させます。デフォルトのままですね。
以下がコードの大枠です。ApplicationController に認証の before_filter を適用条件をつけて仕込みます。
#{RAILS_ROOT}/app/controllers/application_controller.rb
class ApplicationController < ActionController::Base before_filter :login_required, :if => :auth_target? # auth_target? が true の時のみ適用 private def login_required # ログインしていなければログイン画面へリダイレクト end def auth_target? self.controller_name != 'login' end end
#{RAILS_ROOT}/app/controllers/customer_controller.rb
class CustomerController < ApplicationController end
#{RAILS_ROOT}/app/controllers/login_controller.rb
class LoginController < ApplicationController end
注意点としては、:if => の後ろはメソッドにしないといけません。直接 true や false を書いたり、true/false を返す式を書いてもエラーになります。
:unless も使えます。

before_filter の適用条件については前回まとめてます ⇒ [Rails3] before_filter に条件を設定する


namespace を使って(例えば :adminとか)認証の要る・要らないを分ける、というのもよくやられますね。あくまでも認証の要るもの要らないものを区別しているだけなので、実現するには実質的に上のどれかの方法を使うような形になると思います。

結論としては、めんどくさがりな人(=自分?)には④が楽ちんですが、お勧めは②です。
④と違って各コントローラー内のコードで認証の要る・要らないが判断できるのが良いと思います。

まあ、どういうやり方にするかは後は趣味の問題、かな。

0 件のコメント:

コメントを投稿