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
のように簡単にアドバイスの定義ができ、処理を織り込んでいける仕組みがすげぇ。