Railsで認証画面を自作してみた
railsでユーザ認証画面を自作してみた。
さすがに眠いので、このトピックかなり適当になってます。。。。
起きたら清書します、たぶん。。。。
ではいきます。
まずはdbスキーマの定義。migrate後に出来上がる db/schema.rbを載せておきます。
# db/schema.rb # userName に ユーザ名が、password に cryptされたpasswordが入ります。 create_table "authentications", :force => true do |t| t.integer "userId" t.string "userName", :null => false t.string "password", :null => false t.integer "disable" t.datetime "created_at" t.datetime "updated_at" end
続いて authenticationのmodel 定義です。
webを見ていると、modelは単なる O/Rマッパーとして使用し、認証ロジック本体はcontrollerに埋め込んでる例が多い気がしますが
与えられたパラメータ(username, password)が正当性を判定する処理は、ユーザ操作の延長にあるものではなく、modelに問い合わせるもの*1であるため、modelに定義するのが設計的には妥当と思う。
passwordのhashにSHA512なんていうご大層なものを使ってますが、別にString.cryptで十分だったかな(パフォーマンス的にも)。。。
# app/model/authentication.rb require 'digest/sha2' class Authentication < ActiveRecord::Base @@digest = Digest::SHA512.new def Authentication.digest @@digest end def Authentication.crypt target digest.hexdigest target end def Authentication.authorize name, password target = find( :first, :conditions=> { :userName=>name, :password=>crypt(name + ":" + password), :disable=>0 }, :select=>'userId') if target != nil target['userId'] else false end end end
ユーザ登録画面は未実装なので、とりあえず仮ユーザをダミー登録しておきます
# db/seeds.rb Authentication.create( { :userId=>1, :userName=>"admin", :password=>Authentication.crypt("admin:admin"), :disable=>0 } )
次に、controller, view の実装に移ります。
まずはページにアクセスされた際、認証済みかを判定する部分を記述します。
ttp://....:3000/<controler>/<action>/<id> が呼び出されたらユーザ認証が完了しているかを調べます。
ログイン済みであれば、指定されたページをロードします。
ログインしていない場合、ログイン画面にリダレクトします。
railsでは、コントローラのアクションメソッドが呼び出される前に動作するfilterを定義することができます。
ApplicationController内で、before_filterとしてフィルタを定義しておけば、すべてのcontroller/actionで有効なfilterを登録できます。
session[:jumpto] に、リクエストパラメータを保存しておくことで、ログイン後指定ページにリダレクトできるようにしています。
認証が完了しているかの判定には、session[:userName] にユーザ名が入っているかで判定していますが、
これではあまりにもお粗末なので、ちゃんと作るなら、ログイン時にsessionIdを割り当てて、サーバ側でもログイン中のsessionリストを管理する程度のことは必要かと。
# app/controller/application_controller.rb class ApplicationController < ActionController::Base before_filter :authorize def authorize unless session[:userName] session[:jumpto] = request.parameters redirect_to :controller => 'user', :action => 'login' end end end
で、次は ログイン・ログアウトのためのcontrollerクラスです。
skip_filter で、login アクション時は、ログイン済みchekを飛ばしています*2
# app/controller/user_controller.rb class UserController < ApplicationController skip_filter :authorize, :except=>:login def login session[:userName] = nil @user = params['userName'] @password = params['password'] if ( @user != nil ) && ( @password != nil ) if Authentication.authorize(@user, @password) != false session[:userName] = @user flash.discard if session[:jumpto] != nil redirect_to session[:jumpto] else redirect_to({:controller=>'user', :action => 'loggedin'}) end else flash[:notice] = "invalid username or password." end end end def loggedin end def logout session[:userName] = nil end end
で、最後はview部分
ログイン画面は、user name、passowrdが入力されるテキストエリアを貼り付けたformを表示しています。
@user, @password には、直前に入力した user name、password が格納されます。
つまり、user name、passwordのいずれかを間違え、認証に失敗した場合に、前回入力した内容を初期値として表示します。
<% form_tag do %> <p><%= label_tag("Username") %> <%= text_field_tag("userName", @user ) %> </p> <p><%= label_tag("Password") %> <%= text_field_tag("password", @password ) %> </p> <%= submit_tag "Send" %> <% end %>
んで、次が logout画面
logout しました。再びログインするには login をクリックしてください。
みたいな画面です。
<h1>User#logout</h1> <p>Find me in app/views/user/logout.html.erb</p> <%= link_to 'login', {:action => 'login'} %>
最後にログイン完了画面。
とりあえず作っただけ。。。。
<p>User#loggedin</p>