Ruby on Rails 建立用戶系統及加入OpenID
每個Web Application 也為了建立一個安全方便的用戶系統或登入系統而遇上麻煩。Don't Repeat Yourself (DRY), 我們可使用RoR的Plugins解決煩惱!
說到用戶系統,不能不提 MySpace 或 facebook 等公司也非常投入的OpenID 。在RoR 使用OpenId 是非常簡單的事。
Step 1: Signup 登記使用者
先安裝 acts_as_authenticated plugins, 它負責所有基本登入工作。在console 跳至<application_root>
ruby script/plugin install http://svn.techno-weenie.net/projects/plugins/acts_as_authenticated/
安裝後可以用 acts_as_authenticated 建立 "user " Model 和 "account " Controller.
script/generate authenticated user account
這樣便建立了相關的Model 和 Controller。現在設定 Database 。
rake db:migrate
Database 設定完畢,讀者可以檢查使用的 Database 是否有一個新的 Table "users "。所有用戶資料將會放在這裡。
令人喜出望外的是,你已經擁有基本用戶系統的所有編碼,測試時間到了!
ruby script/server
打開 "/account/signup" ,可看到 Signup頁面
打開 /account/signup ,可看到Signup頁面。測試建立一個使用者後,看看資料庫是否更新,完成第一步。
Step 2: 設定登入保護
我們演示登入保護,只要在 Views 和 Controllers 做一點修改便可。
首先,在<application root>/app/views/account 新增 "show.rhtml ",貼上以下編碼。
<%=link_to "log out", "/account/logout"%>
<p><%=current_user.login %> is logged in.</p>
因為acts_as_authenticated 已把基本設定寫妥,我們打開<application root>/app/controllers/account_controller.rb ",在 "include AuthenticatedSystem "下一行插入:
before_filter :login_required, :except => [:login, :signup]
before_filter 設定先決的 action ,這裡我們執行 acts_as_authenticated 的 login_required action。所以這 controller 的所有 actions 也有預設登入保護。
因為我們加入了 :except => [:login, :signup] ,那麼 login 和 signup 兩個頁面也不會被登入保護,沒有用戶的人仕也可瀏覽這兩個頁面。
測試!在瀏覽器鍵入 http://localhost:3000/account/show
show 被登入保護了
Ooops,被送到 "login " 頁面。因為"show " 被登入保護了。這時候可以登入剛才新增的使用者:
登入後被送到 show
成功登入後便會自動送到"show "。
現在你的application 已有一個完整的登入系統了!
**如果你的 "account_controller.rb" 有其他 actions ,或者 "/views/account" 有其他頁面,也會有登入保護的了。
Step 3: OpenID
終於到精采部份了,就是為加入OpenID! 如果你不使用OpenID ,可以跳過這部份。但這樣做之前要留意,OpenID 是一個十分成功的共用登入系統,MySpace 和 facebook 也參與其中。以下是OpenId 編碼
安裝 ruby-openid RubyGems
sudo gem install ruby-openid -y
安裝 restful_authentication
script/plugin install http://svn.techno-weenie.net/projects/plugins/restful_authentication
安裝 open_id_authentication
這個 Plugins 和Rails 2.x 的相容問題較多。筆者發現一個較好的版本,不過它放在 github 裡。要先到 http://github.com/rails/open_id_authentication/tree/master 下載。解壓後改名為open_id_authentication 文件夾,並把它移到<application root>/vendor/plugins 內。
現在共有三個Plugins: acts_as_authenticated, restful_authentication, open_id_authentication
建立新 Tables
open_id_authentication 要兩個Database Tables 來記錄OpenId 資料,執行以下指令來建立。
rake open_id_authentication:db:create
rake db:migrate
open <application root>/config/routes.rb ; 確保以下編碼正在運作。
map.root :controller => 'account'
Views 修改
先修改"<application root>/app/views/account/login.rhtml " ,在 "<p><%= submit_tag 'Log in' %></p>" 上面插入:
<p> use OpenID ... </p>
<p> <%= text_field_tag "openid_url" %> </p>
再把"<application root>/app/views/account/show.rhtml" 改寫成:
<%=link_to "log out", "/account/logout"%>
<% if current_user.not_openid?%>
<p><%=current_user.login %> is logged in.</p>
<% else %>
<p><%=current_user.identity_url %> is logged in as an open id user.</p>
<% end %>
Models修改
Model 只有一個相關的 <application root>/app/models/user.rb 。但這 user.rb 檔案有幾處要修改。
1. 加入 def not_openid?
def not_openid?
identity_url.blank?
end
2. 修改def password_required?
not_openid? && (crypted_password.blank? or not password.blank?)
end
3. 加入":if => :not_openid?" 至幾個validations,讓openid 使用者避開該項核對。
validates_presence_of :login, :email, :if => :not_openid?
validates_length_of :login, :within => 3..40, :if => :not_openid?
validates_length_of :email, :within => 3..100, :if => :not_openid?
Controllers 修改:
只須修改"<application root>/app/controllers/account_controller.rb" 。這檔案也要修改幾處地方。
1. 修改 login ,以加入OpenId 登入:
def login
return unless request.post?
if using_open_id?
open_id_authentication(params[:openid_url])
elsif params[:login]
password_authentication(params[:login], params[:password])
end
end
3. 修改password_authentication (如有需要的話)
def password_authentication(login, password)
if self.current_user = User.authenticate(params[:login], params[:password])
successful_login
else
failed_login("Invalid login or password")
end
end
4. 加入 open_id_authentication
def open_id_authentication(openid_url)
authenticate_with_open_id openid_url, :required => [:nickname, :email] do |result, identity_url, registration|
if result.successful?
@user = self.current_user = User.find_or_create_by_identity_url(identity_url)
if @user.new_record?
# registration is a hash containing the valid sreg keys given above
# use this to map them to fields of your user model
{'login=' => 'nickname', 'email=' => 'email'}.each do |attr, reg|
current_user.send(attr, registration[reg]) unless registration[reg].blank?
end
if @user.valid?
@user.save
self.current_user = @user
successful_login
else
failed_login "Authentication failed on this website."
end
else
self.current_user = @user
successful_login
end
else
failed_login result.message
end
end
end
這個 action 精明地完成OpenId 核對,並把一些項目從OpenId Server 取回(假設你的OpenId 戶口有:nickname 和 :email 這兩項資料) 。如果是application 的新用者,這些變數將自動用作建立新用者的設定,即是一個新用者也完全不須使用signup 頁面。
5. 加入兩個跳頁用的action
private
def successful_login
redirect_back_or_default({:action => :show})
flash[:notice] = "Logged in successfully"
end
def failed_login(message)
redirect_to(:action => 'login')
flash[:warning] = message
end
6. 設定 Database 。
打開console ,跳到在 <application root>
ruby script/generate migration AlterUserIdentityUrl
打開<application root>/db/migration/<新的編號_alter_user_identity_url.rb> ,改寫為:
class AlterUserIdentityUrl < ActiveRecord::Migration
def self.up
add_column :users, :identity_url, :string
end
def self.down
remove_column :users, :identity_url
end
end
rake db:migrate
完成!可以測試了。 當然,讀者要先登記一個OpenId (詳情請參閱 http://openid.net/ )
在http://localhost:3000/account/login ,我用一個沒有在localhost登記的OpenId 。
先輸入你的Open Id ,然後按Submit
登入後,會自動跳到OpenId Server,這時要登入OpenId 一次。
你會被送到OpenId Server,輸入你的OpenId password ,並按登入
成功後,你便會使用OpenId 登入localhost ,並跳至相關頁面。
用OpenId 跳至show頁面
總結
在建立一個使用者系統,acts_as_authenticated Plugins 帶給我們很大的方便。"script/generate authenticated user account " 已經把大部份編碼完成。
之後我們使用ruby-openid gem 和 open_id_authentication ,它們使存取OpenId 極之方便。甚至新的使用者也可直接登入系統!只要在 OpenId 登入一次後,那麼以後到任何一個你信任的 OpenId 網站也不用再次登入或登記了。