在 Rails project 上導入 OpenID

本文用的 ruby-openid 是 2.0.4 版本,目前的 acts_as_authenticated plugin 只有整合到 ruby-openid 1.1.4 版本。

OpenID 的介紹在之前的文章就已經寫過了,這篇文章主要是記錄一下我在 Rails 上使用 ruby-openid 這個 gem 整合 OpenID 的過程。以下就以 step by step 的方式作介紹:

  1. 安裝 ruby-openid 這個 gem: gem install ruby-openid
  2. 在負責處理登入驗證的 controller 中,加入 auth_by_openid 這個 action:

    [code lang="ruby"]
    require 'openid'
    require 'openid/store/filesystem'

    ...

    def auth_by_openid(openid)
    @@store ||= OpenID::Store::Filesystem.new("#{RAILS_ROOT}/tmp/_openid")
    begin
    c = OpenID::Consumer.new(session, @@store)
    req = c.begin(openid)
    if req.nil?
    flash[:error] = "Open ID fail"
    redirect_to_signin
    else
    redirect_to req.redirect_url("", url_for(:action => "confirm", :controller => "account"))
    end
    rescue OpenID::DiscoveryFailure
    flash[:error] = "OpenID 格式錯誤"
    redirect_to_signin
    end
    end
    ...
    [/code]

    因為 OpenID 的認證必須要有地方存著 session 資料,目前的 ruby-openid 有提供一個儲存在 file system 的 module,所以 @@store 就是用來表示儲存 session 的物件。

    接著用 OpenID::Consumer 來操作 OpenID consumer 的動作,begin 這個 method 會去 OpenID server 詢問一下傳入的 OpenID 是哪個單位的,如果成功,則會把要導向的 URL 記在回傳值中,所以我們必須用 redirect_to 把 user 導向它 OpenID 驗證的頁面去作帳號密碼驗證。

    req.redirect_url 第一個參數應該是「認證過的網站」的一個代碼,如果你的網站沒有被 OpenID 認證過,這裡可以代長度為0的字串,而第二個參數則是:當 OpenID 認證完畢後,要導回到你網站的哪個位置,所以我們還要再用一個 action 來處理 OpenID 認證完的結果。

    因為用戶可能輸入的不是合法的 OpenID,所以要處理 OpenID::DiscoveryFailure 這個 exception。

  3. 根據上述的步驟,還要再定義一個 action 來處理驗證完畢的確認:

    [code lang="ruby"]

    def confirm
    @@store ||= OpenID::Store::Filesystem.new("#{RAILS_ROOT}/tmp/_openid")
    c = OpenID::Consumer.new(session, @@store)
    response = c.complete(params.reject{|k,v|request.path_parameters[k]}, url_for(:action => "confirm", :controller => "account"))

    if response.status == OpenID::Consumer::SUCCESS
    # 成功
    else
    # 失敗
    end
    end

    [/code]

    簡單地說,在驗證的時候是用 OpenID consumer 的 begin,在確認這個步驟時是用 complete,最後再檢查 response.status 的 status code 就可以判斷 OpenID 的驗證是否成功了。

因為最近比較沒空,我就沒有把這樣的動作整合到 acts_as_authenticated 這個 plugin 裡面了,有興趣的人可以試著去做做看 😛