railsのActionControllerでBasic認証
rails自体で簡単にBasic認証を使いたいときのため、Filterで実装してみた。
http://blogs.23.nu/c0re/stories/7409/
の改造版。
class BasicAuthFilter def initialize(*options_tmp) options = options_tmp.pop @username = options[:username] @passwd = options[:passwd] @realm = options[:realm] || 'Rails Basic Auth' @error_msg = options[:error_msg] || 'auth error' end def before(controller) username, passwd = get_auth_data(controller.request) headers = controller.headers if username != @username || passwd != @passwd headers["Status"] = "Unauthorized" headers["WWW-Authenticate"] = "Basic realm=\"#{@realm}\"" controller.render :text => @error_msg, :layout => false, :status =>'401' end end # before afterの定義が必要みたいなので。 def after(controller) end private def get_auth_data(request) username, passwd = '', '' if request.env.has_key? 'X-HTTP_AUTHORIZATION' # X-HTTP_AUTHORIZATIONがヘッダにあったら authdata = request.env['X-HTTP_AUTHORIZATION'].to_s.split elsif request.env.has_key? 'HTTP_AUTHORIZATION' # HTTP_AUTHORIZATIONがヘッダにあったら authdata = request.env['HTTP_AUTHORIZATION'].to_s.split end # Basic認証なヘッダなら if authdata and authdata[0] == 'Basic' username, passwd = Base64.decode64(authdata[1]).split(':')[0..1] end [username, passwd] end end
でフィルターを作る。んでコントローラーのaround_filterに登録
around_filter BasicAuthFilter.new(:username => 'rails',:passwd =>'foo')
usernameとpasswdは必須。他にrealm(ダイアログ表示メッセージ)と@error_msg(認証失敗時の表示メッセージ)をnewの引数で渡せる。
アクションfooとそれ以外で違うユーザ名、パスワードを使いたい場合は
around_filter BasicAuthFilter.new(:username => 'rails',:passwd =>'foo_pass'), :only => 'foo' around_filter BasicAuthFilter.new(:username => 'rails',:passwd =>'bar_pass'), :except => 'foo'
のようにonly、exceptで定義すればOK。これは標準のFilterの機能だね。ActionControllerのFilterってジョインポイントはbefore,afterしか今のところ定義できないけど、アスペクト指向だねぇ。Rubyのシンタックスをうまく利用して
class FooController < ApplicationController before_filter :set_charset end
のように簡単にアドバイスの定義ができ、処理を織り込んでいける仕組みがすげぇ。