インテリジェントな認証システムの実現
ここからはアプリケーションコードを修正し、ログインコントローラーを実装して、認証システムを構築していきます。
アプリケーションコードの修正
- 「app/controllers/application.rb」ファイルを開き、次の
authorizeメソッドを追加します。 - 次に、枠組みと共に作成された「todos_controller.rb」を修正する必要があります。下記のように、
before_filterを追加します。
def authorize unless session[:user_id] flash[:notice] = "Please log in" # save the URL the user requested so we can hop back to it # after login session[:jumpto] = request.parameters redirect_to(:controller => "/login", :action => "index") end end
:jumptoシンボルに格納してログイン画面を生成します。ユーザーはログインするとすぐに、最初に要求したURLに転送されます。class TodosController < ApplicationController before_filter :authorize, :only => [ :new , :edit, :create, :update, :destroy] ... rest of the controller as before ... end
ログインコントローラーによる認証の仕組み
ログインコントローラを実装することにより、ログインの仕組みの最重要部を確認できます。端末で次のコマンドを実行します。
# script/generate controller login
生成された「app/controllers/login_controller.rb」ファイルを開きます。このファイルをリスト1のように編集します。
require 'ostruct' class LoginController < ApplicationController def index redirect_to :controller => "todos" if session[:user_id] end def login if using_open_id? authenticate else flash[:error] = "You must provide an OpenID URL" redirect_to :action => "index" end end def logout session[:user_id] = nil redirect_to :action => "index" end protected def authenticate(identity_url = "") authenticate_with_open_id( params[:openid_url], :required => [:nickname, :email]) do |result, identity_url, registration| if result.successful? @user = OpenStruct.new @user.identity_url = identity_url @user.nickname = registration["nickname"] @user.email = registration["email"] session[:user_id] = @user jumpto = session[:jumpto] || { :controller => "todos" } session[:jumpto] = nil redirect_to(jumpto) else flash[:error] = result.message redirect_to :action => "index" end end end def root_url openid_url end end
このコントローラには認証の仕組みがすべて含まれているので、順番に見ていきましょう。
indexメソッド
indexメソッドは、ユーザーが既にログインしている場合はユーザーをWebサイトのメインページ(http://localhost:3000/todos)にリダイレクトし、まだログインしていない場合はログインページ(http://localhost:3000/login)に転送します。まだログインページのビューがありませんので、次のコードを使用して「app/views/login/index.html.erb」ファイルを作成します。
<% if flash[:error] -%>
<%= flash[:error] %>
<% end -%>
<% form_tag :controller => "login" , :action => "login" do |f| -%>
<label for="openid_url" >
OpenId URL:
</label>
<%= text_field_tag :openid_url -%>
<%= submit_tag "Login" -%>
<% end -%>
これを見ると、従来のようにユーザー名とパスワードを入力するフィールドではなく、ユーザーのOpenID Identifierを受け付けるテキストフィールドが1つだけあります。openid_urlというラベルは慣用的なラベルで、これがあることでブラウザはユーザーのOpenID IdentifierをOpenID対応のWebサイト全体で記憶できます。
loginメソッドとlogoutメソッド
loginメソッドはログイン処理を扱い、logoutメソッドはアプリケーションからログアウトしたユーザーのデータをセッションからすべて削除します。
using_open_id?関数はruby-openidライブラリに属する関数で、ユーザーがログインを開始中または終了中であるかどうかを検出します。図3に示すように、ログイン処理ではWebサイトに対して呼び出しが2回行われます。1回目の呼び出しにはログイン処理を開始するOpenID Identifierが含まれ、2回目の呼び出しにはOpenID Providerから返されたユーザー資格情報が含まれます。従って、loginメソッドは2回呼び出され、using_open_id?関数は両方の発生を検出します(これについては、後で詳しく説明します)。

authenticate関数
authenticate関数には認証ロジックのほとんどが記述されています。これは、ruby-openidライブラリのauthenticate_with_open_id関数に委任されます。この関数はログイン処理が正常に完了したかどうかに関係なく、その完了時(つまり、Webサイトへの2回目のコールバックの後)に実行されるブロックを受け付けます。このブロックには、次の3つのパラメータを指定できます。
- resultオブジェクト
- identity_urlオブジェクト
- registrationオブジェクト
Simple Registration ExtensionはOpenID仕様に追加された機能で、ProviderとWebサイトでユーザーに関する追加情報を交換できるようにします。例えば、ユーザーの電子メールアドレス、本名、生年月日、その他の個人データを交換できます。ユーザーは個人情報の非開示をOpenID Providerに要求してプライバシーを保護できます。前の例では:optionalディレクティブではなく:requiredディレクティブを使用して、ユーザーの資格情報以外にもニックネームと電子メールアドレスを要求しています。
OpenIDによりSREGでも認証機能が向上します。従来の仕組みを採用したWebサイトでは、ユーザーは登録フォームに必要事項をすべて入力する必要があり、サードパーティごとに個人データを何度も繰り返し指定しなければなりません。
最後に、authenticateメソッドは:user_idセッション変数にユーザーデータをすべて格納し、redirect_to(:jumpto)文によってユーザーを最初の要求ページにリダイレクトします。
routes.rbファイルとroot_urlメソッドの設定
前述したように、loginメソッドは2回呼び出されます。2回目の呼び出しでは、OpenID Providerからユーザーブラウザへのリダイレクトが指示されます。このリダイレクトを正しく行うには、最後に2つの処理を実行する必要があります。
まず、次のように「routes.rb」構成ファイルに適切な経路を定義する必要があります(名前付き経路を使用していることに注意してください)。
map.openid "login", :controller => "login" , :requirements => { :method => :get }
次に、その名前付き経路をログインコントローラのroot_urlメソッドで参照する必要があります。
def root_url openid_url end
これにより、OpenID Providerに対して、ログイン後にユーザーがリダイレクトされるURLは、ログイン要求の送信元ドメインと同じドメイン(信頼されたルート)に属することが保証されます。
最後の処理が完了すると、適切に認証され識別されたユーザーのみ、アプリケーションは受け付けるようになります。また、追加情報を使用してユーザーにさらに便利な機能を提供することもできます。
例えば、ユーザーが新しいTODO項目を作成するときに、ユーザー名と電子メールアドレスを既に入力しておくことができます。これを行うには、「todos_controller.rb」ファイルを再度開きnewメソッドを次のように修正します。
def new @todo = Todo.new @todo.person = session[:user_id].nickname @todo.email = session[:user_id].email respond_to do |format| format.html # new.html.erb format.xml { render :xml => @todo } end end
