認証機能確認

OpenIDが気になっていたので、それのサンプルを作ってみる。RailsにHatenaとLiveDoorの認証を組み込んだことがなかったので、そちらも併せて組み込んでみておく。この3つができればメジャーどころはかなりサポートできるとおもう。

OpenID対応をRailsに組み込む

前提

  1. Rails1.2.6
  2. Ruby1.8.6 P114

準備

#/usr/local/ruby1.8/bin/gem install ruby-openid
(2.0.4がインストールされた)

OpenID1.0と2.0両方に対応している模様

概念

基本的な概念は他の認証とさほど変わりない。おおよそ以下の流れとなる

入力ページ->情報作成してロケーション->情報受け

構築

1.入力ページ

ちょっと面倒な部分もあるのでソースを見ながら確認する

ログイン部分は以下のような感じ。見事に何もない

 require "openid"
 require 'openid/extensions/sreg'
 require 'openid/extensions/pape'
 require 'openid/store/filesystem'
 class UserController < ApplicationController
   def index    
     login
     render :action => 'login'
   end
 
   def login
   end
 end

Viewも全然たいしたことをしてない

user/login.rhtml
 <p>OpenID ログインテスト</p>
 <form method="get" accept-charset="UTF-8"
   action='<%= url_for :action => 'start' %>'>
   <p>  open-ID: <input type="text" class="openid" name="openid_identifier" /></p>
   <p> 種別 <%=radio_button_tag "id_type",0,true%>OpenID 
            <%=radio_button_tag "id_type",1,false%>Hatena 
            <%=radio_button_tag "id_type",2,false%>LiveDoor </p>
   <input type="submit" value="ログイン" />
 </form>

openIDのなかで、HatenaとLiveDoorは処理を分けてある。これはHatenaとLiveDoorのIDさえ入力してくれればURL部分は補助しようというのが目的なので実態として合ってもなくても問題無い

2.情報作成からロケーション

この部分が他の認証より一回り面倒くさい。ただ、一回作ってしまえば後は同じなので、まあ定型処理といえる

 class UserController < ApplicationController
   def start
     begin
       # ユーザが入力したopenid_identifierを元にOpenIDリクエストを作成
       # HatenaとLiveDoorの時は頭のhttp://...を付与するだけ
       id_url = ""
       if 1 == params[:id_type].to_i
         id_url = "http://www.hatena.ne.jp/" + params[:openid_identifier] + "/"
       elsif 2 == params[:id_type].to_i
         id_url = "http://profile.livedoor.com/" + params[:openid_identifier] + "/"
       else
         id_url = params[:openid_identifier]
       end
       #ロケーションする先のurlを作る
       oidreq = consumer.begin(id_url) 
 
       # 認証サーバ(OP)からの戻り先URL(completeアクションのURL)
       return_to = url_for :action => 'complete', :only_path => false  
       # このサーバを識別するためのrealm
       realm = url_for :action => 'index', :only_path => false
       # 利用者をOPのログイン画面(OP EndPoint)へと誘導する
       redirect_to(oidreq.redirect_url(realm, return_to))
     #エラーを補足する
     rescue OpenID::DiscoveryFailure
       @error = "OpenID::DiscoveryFailure";
     end
  end
  #この下が不思議な処理。サンプルから取得したんだが、ローカルに一端セッションを自力で
  #作っているらしい。非常に奇妙だ。分散サーバーにしたときなど大丈夫なんだろうか
  private
  def consumer
    if @consumer.nil?
      dir = Pathname.new(RAILS_ROOT).join('db').join('cstore')
      store = OpenID::Store::Filesystem.new(dir)
      @consumer = OpenID::Consumer.new(session, store)
    end
    return @consumer
  end
 end

3.情報受けページ

ロケーションするときに戻りページを指定しておいたので、completeアクションがコールされる。

 class UserController < ApplicationController
   def complete
     #パラメータを作成
     parameters = params.reject{|k,v|request.path_parameters[k]}
     
     # いま自分がいるURLがデータ取得に必要らしいので、作成
     current_url = url_for(:action => 'complete', :only_path => false)
     
     # 戻り値を解析し,OpenID::Responseクラスのオブジェクトを作成
     @oidresp = consumer.complete(parameters, current_url)
     
     #ステータスによってログインの成功不成功をキャッチ
     if OpenID::Consumer::SUCCESS == @oidresp.status 
       @mes = "login 成功"
     elsif OpenID::Consumer::CANCEL == @oidresp.status 
       @mes = "ログインがキャンセルされました"
     else
       @mes = "認証に失敗しました"
     end
   end
 end

ログインに成功するとOpenID::Responseオブジェクトが作成でき以下のメソッドでユーザを特定できるID(URL)が取得できる。これでログイン完了となる。他の要素については特にもらえない

 @oidresp.identity_url

プライベートメソッドがすこし気持ちわるいが、Hatena,YahooのOpenID1.0/2.0ともにこれでデータがとれた。ライブドアはどうしてもダメだったが他のサイトのOpenIDでのログインで試してもダメなので他の要素が何かあるのだろう。