2008年11月10日 星期一

Merb-Auth (3) 再來一種 Strategy

上一篇講了設定和簡單的流程原理。這一篇又要來寫些程式
先來看看最基本的 Strategy 大概的樣子:
class MyStrategy < Merb::Authentication::Strategy
def run!
#這裡要寫登入的邏輯,成功時傳回登入物件,失敗時傳回 false 或 nil
end
end
每個 Strategy 都會繼承 Merb::Authentication::Strategy 這個 class,並且有一個名為 run! 的 method。當 Merb-Auth 要 Login 時,就會一個一個 Strategy 的去跑這個 Method。當此 method 回傳一個非 false 的值時,Merb-Auth 認為登入成功。如果失敗的話,可以回傳 nil/false ,或者也可直接 redirect! 到某個位置,對過接下來其它的 Strategy (OpenID 就是這麼做的)。

讓我們把 Hello World 加上 OpenID 的功能吧!
首先要改變一下 User model,將 app/models/user.rb 加上一個 property,用來存此使用者所用的 openid:
property :identity_url, String, :length => 256
在 app/views/exceptions/unauthenticated.html.erb 加上 openid 專用的 form:
<div>
<form action="<%= slice_url(:merb_auth_slice_password, :perform_login) %>" method="POST" accept-charset="utf-8">
<input type="hidden" name="_method" value="PUT" />
<div class="formRow">
<label>OpenID
<input type="text" name="openid_url" value="" id="openid_url">
</label>
</div> <!-- close: formRow -->
<div class="formRow">
<input type="submit" name="Submit" value="登入" id="Submit">
</div> <!-- close: formRow -->
</form>
</div>
這個 form 最主要是讓使用者輸入他的 openid 到 openid_url 這個 field 裡去,其它都照抄原來的 password form。

最後在 merb/merb-auth/strategies.rb 裡加上這幾行:
Merb::Authentication.activate!(:default_openid)
class Merb::Authentication::Strategies::Basic::OpenID
def openid_callback_url
"#{request.protocol}://#{request.host}/"
end

def on_success!(response, sreg_response)
if user = find_user_by_identity_url(response.identity_url)
user
else
user = user_class.new
user.login = sreg_response.data['nickname']
user.identity_url = response.identity_url
user.password = user.password_confirmation = Digest::SHA1.hexdigest("#{response.identity_url}#{rand(1000)}")
user.save ? user : nil
end
end
end
第一行是將 default_openid 這個 strategy 開啟。接下來是 overwrite 它的一些 default 設定。

openid_callback_url 的回傳值是給 openid provider redirect back 用的。基本上就是一個需要 login 的 url,我們的例子中就用 http://localhost:4000/ 就可以了。

openid provider 回傳此使用者可以登人後,就會呼叫 on_success! 。
首先我們先用 find_user_by_identity_url 去找有沒有這個 user object,如果就就表示此人曾經用這個 openid 登入過,直接回值這個 user 就好了。如果沒有的話,就用建立一個新的使用者。(這裡這麼做是為了簡化起見,正式的做法應該是 redirect! 到一個註冊的頁面,讓使用者可以輸入/更改一些像是暱稱、生日之類的訊息。)

這樣就寫好了 opendid 支援。

沒有留言: