authlogic笔记

参考:
http://github.com/binarylogic/authlogic_example
http://www.binarylogic.com/2008/11/16/tutorial-reset-passwords-with-authlogic/
http://railscasts.com/episodes/170-openid-with-authlogic

Authlogic引入了一种新的model,它继承自Authlogic::Session::Base,是专门用来处理会话细节的。就像ActiveRecord是专门用来处理数据库相关细节一样。你可以像对待普通的ActiveRecord model一样来创建、更新和删除会话。

安装:
script/plugin install git://github.com/binarylogic/authlogic.git

简单的一个入门demo:

首先是注册功能,得先注册,然后才可以登录嘛。
新建一个user model
User可能会需要下面这些字段,其中:persistence_token是必须的,其它都是可选的,按情况做出自己的选择:
    t.string    :login,               :null => false                # 可选,可以用email来代替它,或者同时使用两者。
    t.string    :email,               :null => false                # 可选,可以用login来代替它,或者同时使用两者。
    t.string    :crypted_password,    :null => false                # 可选,但强烈建议加上。
    t.string    :password_salt,       :null => false                # 可选,但强烈建议加上。
    t.string    :persistence_token,   :null => false                # 必选
    t.string    :single_access_token, :null => false                # 可选,详见Authlogic::Session::Params
    t.string    :perishable_token,    :null => false                # 可选,详见Authlogic::Session::Perishability

    # Magic columns,就像ActiveRecord的created_at和updated_at一样,这些字段由Authlogic自动维护。
    t.integer   :login_count,         :null => false, :default => 0 # 可选,详见Authlogic::Session::MagicColumns
    t.integer   :failed_login_count,  :null => false, :default => 0 # 可选,详见Authlogic::Session::MagicColumns
    t.datetime  :last_request_at                                    # 可选,详见Authlogic::Session::MagicColumns
    t.datetime  :current_login_at                                   # 可选,详见Authlogic::Session::MagicColumns
    t.datetime  :last_login_at                                      # 可选,详见Authlogic::Session::MagicColumns
    t.string    :current_login_ip                                   # 可选,详见Authlogic::Session::MagicColumns
    t.string    :last_login_ip                                      # 可选,详见Authlogic::Session::MagicColumns

注意,login、email、crypted_password字段都是可选的,是因为Authlogic并不关心登录认证的实现方式。如果你喜欢,可以使用OpenID、LDAP或者其它任何你想要的方式,甚至不提供你自己的认证系统。

与给每个任务(例如重置密码)提供一个token的方式不同,authlogic为不同需求的任务提供了几种不同的token:

    * Persistence token: Authlogic内部使用的。它存储在你的cookies和session里,用于持久化当前用户的会话。这种方式比单纯的保存user id要安全得多。
    * Single access token: 用于私有的API访问。比如:www.whatever.com?user_credentials=[single access token] 授权访问但不持久化。
    * Perishable token: 重设密码、激活账号时很有用。

更多详细内容可以查看 文档

要做的这个demo除了single_access_token和perishable_toker,其它似乎都用得到:
script/generate model user email:string crypted_password:string password_salt:string persistence_token:string login_count:integer failed_login_count:integer last_request_at:datetime last_login_at:datetime current_login_at:datetime last_login_at:datetime current_login_ip:string last_login_ip:string



rake db:migrate

为了使用authlogic来做这个注册功能,给user model加上一句:
class User < ActiveRecord::Base
  acts_as_authentic
end

接着创建一个UsersController
script/generate controller users

定义路由:
map.resource :account, :controller => 'users'

给UsersController添加new方法:
def new
  @user = User.new
end

views/users/new.html.erb
<% form_for @user, :url => account_path do |form| %>
  <%= form.error_message %>
  <%= form.label :email %>
  <%= form.text_field :email %><br/>
  <%= form.label :password %>
  <%= form.password_field :password %><br/>
  <%= form.label :password_confirmation %>
  <%= form.label :password_confirmation %><br/>
  <%= form.submit 'sign up'%>
<% end %>

user model acts_as_authentic之后,如果user model有crypted_password和password_salt字段,authlogic会为user model自动添加上password和password_confirmation这两个虚拟属性。
给UsersController添加create方法:
def create 
  @user = User.new params[:user]
  if @user.save
    flash[:notice] = 'account create successful'
    redirect_to root_path
  else
    render 'new'
  end
end

user model acts_as_authentic之后,authlogic会自动的为user model加上一些验证,比如login必填、login唯一、email格式、email必填、email唯一、password必填、password长度、password_confirmation和password必须相同等……
可以启动服务器,访问account/new,填写一些不合法的数据,提交看看效果。
为了使用户在注册成功后能重定向到root_path,现在添加一个简单的controller、view和route规则:
script/generate controller welcome index

views/welcome/index.html.erb
welcome!

map.root :controller => 'welcome'

为了能够成功访问到这个welcome/index.html.erb,现在把public/index.html删除。
现在访问注册页面:account/new。往表表单里填写合法的注册信息,然后提交。可以看到重定向到了root_path。
注册功能写好之后,需要一个登录功能。
登录,也就是创建一个会话(Session),我们需要一个新的模型,给它起个名字,叫UserSession吧。authlogic带了这么一个模型,我们会发现它用起来跟ActiveRecord差不多,接口非常像。
首先创建一个session模型,这里不再使用model这个generator,而改用authlogic 提供的session这个generator。
script/generate session user_session

可以看到生成的UserSession继承自authlogic的Authlogic::Session::Base。
class UserSession < Authlogic::Session::Base
end

接下来创建一个controller来操作这个Session模型。
script/generate controller user_sessions

定义一个路由规则:
map.signin '/signin', :controller => 'user_sessions', :action => 'new'

为UserSessionsController定义一个new方法,像普通的RESTful Controller一样,这个new方法返回一个新的UserSession对象以便生成创建会话的表单:
def new
  @user_session = UserSession.new
end

views/user_sessions/new.html.erb这个表单也和普通的RESTful表单一样:
<% form_for @user_session do |form| %>
  <%= form.error_messages %>
  <%= form.label :email %>
  <%= form.text_field :email %><br/>
  <%= form.label :password %>
  <%= form.password_field :password %><br/>
  <%= form.submit 'sing in' %>
<% end %>

这时候如果通过/signin 访问这个表单,会报user_sessions_path方法找不到的错。我忘记给UserSessionsController定义路由规则了:
map.resource :user_session #对用户来说,session只有一个,所以这里是单数形式的resource。

然后把上面的表单改成这样(详见单数形式resource生成的RESTful路由规则):
<% form_for @user_session, :url => user_session_path do |form| %>

接着刷新/signin页面,可以正常显示了。填入email和密码,登录。
No action responded to create.

创建会话还需要一个create方法:
  def create
    @user_session = UserSession.new params[:user_session]
    if @user_session.save
      flash[:notice] = 'sign in successful!'
      redirect_to root_path
    else
      render 'new'
    end
  end

ActiveRecord的save方法把模型保存到数据库。Session模型的save方法则是把模型保存到http session中。并且,和ActiveRecord一样,在save的时候会触发一些验证。#FIXME

这回应该没问题了,再次登录……先随便输入一些不合法的登录信息吧,可以看到authlogic对Session模型也自动加上了各种验证。现在输入刚才注册时填写的正确的email和密码,点击sign in,可以看到成功的跳转到了root_path页面。

如何知道我们是不是真的登录成功了呢?试着把用户的email输出在welcome页面吧。
我们需要一个取到当前登录的用户的方法
app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  helper :all # include all helpers, all the time
  protect_from_forgery # See ActionController::RequestForgeryProtection for details

  # Scrub sensitive parameters from your log
  # filter_parameter_logging :password

  private

  def current_user_session
    @current_user_session ||= UserSession.find
  end

  def current_user
    @current_user ||= current_user_session && current_user_session.record
  end
end

这里的UserSession.find会返回当前的session对象,当然,是UserSession包装的session,不是rails的session,其中保存着一个当前用户的id。UserSession的record方法会发起一次SQL查询,查找这个id对应的User并返回。

要注意的是,这里的这两个current_*方法定义在了ApplicationController里,页面上无法直接访问Controller里的方法。那么把它们移到WelcomeHelper中吗?也不行,helper里取不到session。这时候可以使用helper_method,像这样:
class ApplicationController < ActionController::Base
  helper :all # include all helpers, all the time
  protect_from_forgery # See ActionController::RequestForgeryProtection for details

  # Scrub sensitive parameters from your log
  # filter_parameter_logging :password

  helper_method :current_user_session, :current_user

  private

  def current_user_session
    @current_user_session ||= UserSession.find
  end

  def current_user
    @current_user ||= current_user_session && current_user_session.record
  end
end

helper_method是rails中的一个方法,可以让controllers里的方法像helper一样给views调用。我认为这个方法不可以乱用,像这两个方法一样即可以让页面调用,又可以让controller调用的情况应该不多。views需要的方法应该多属于表现逻辑,都应该放在各个helper module中。

另外,application_controller.rb的注释部分有一个filter_parameter_logging方法。搜索了一下,找到这个: http://hideto.iteye.com/blog/106956
之前andyhu1007的Rails每周一题第七篇中也有提到: http://www.iteye.com/topic/378559

现在修改一下 views/welcome/index.html.erb以显示当前用户的email:
welcome <%= current_user && current_user.email '' %>!

刷新页面,可以看到当前用户的email被显示出来了。

添加一个注销功能,也就是销毁会话:
controllers/user_sessions_controller.rb
def destroy
  current_user_session.destroy
  redirect_to signin_path
end

views/welcome/index.html.erb
welcome <%= current_user && current_user.email '' %>!<br/>
<%= link_to 'logout', user_session_path, :method => 'delete' %>

访问root_path,点击这个logout,注销成功。再返回root_path,welcome后面的email没了。

其实authlogic在用户注册成功之后,会自动登录一次。现在去新注册一个用户,会发现注册成功后已经是登录状态了。

添加一个显示和修改用户资料的功能。
先加一个show的页面:
views/users/show.html.erb
email:<%= @user.email %><br/>
login count:<%= @user.login_count %><br/>
failed login count:<%= @user.failed_login_count %><br/>
last request at:<%= @user.last_requrest_at %><br/>
current login at:<%= @user.current_login_at %><br/>
last login at:<%= @user.last_login_at %><br/>
current login ip:<%= @user.current_login_ip %><br/>
last login ip:<%= @user.last_login_ip %><br/>
<%= link_to 'edit', edit_account_path(@user)%>
<%= link_to 'logout', user_session_path, :method => 'delete' %>

和show action
controllers/users_controller.rb
def show
  @user = current_user
end

把之前写的UsersController的create方法改一改:
def create 
  @user = User.new params[:user]
  if @user.save
    flash[:notice] = 'account create successful'
    redirect_to account_path #改一下这里
  else
    render 'new'
  end
end

现在试着新注册一个用户,注册成功后会跳转到显示用户资料的页面,下面有一个edit链接。

然后是edit.html.erb页面,为了偷懒,这里把之前new的表单提取出来。
views/users/_form.html.erb
  <%= form.error_message %>
  <%= form.label :email %>
  <%= form.text_field :email %><br/>
  <%= form.label :password %>
  <%= form.password_field :password %><br/>
  <%= form.label :password_confirmation %>
  <%= form.label :password_confirmation %><br/>
  <%= form.submit 'sign up'%>

views/users/new.html.erb
<% form_for @user, :url => account_path do |form| %>
  <%= render :partial => 'form', :object => form %>
<% end %>

views/users/edit.html.erb
<% form_for @user, :url => account_path do |form| %>
  <%= render :partial => 'form', :object => form %>
<% end %>

为UsersController添加一个edit和update方法
def edit
  @user = current_user
end

def update
  @user = current_user
  if @user.update_attributes params[:user]
    flash[:notice] = 'account update successful!'
    redirect_to account_path
  else
    render 'edit'
  end
end

现在可以修改用户的email了。如果密码留空,authlogic默认不会更新密码,但这个是可以选择的,如果你希望用户在密码留空并提交的时候把密码清空,可以这样修改user model:
acts_as_authentic do |config|
  config.ignore_blank_passwords = true
end

不过这个不好试验,还要把 authlogic对密码为空时的表单验证给去掉。我是在写测试代码的时候遇到这个问题才注意到的。嗯,可以在rails console里试试。

……吃完饭写到21点多。接下来的简单记记:

如果user model中定义了active?, approved?, and confirmed?这3个方法中的任意一个,authlogic在用户登录时,会自动调用该方法,如果返回值为false,则Session创建失败,即登录失败。

邮件激活的功能大概是这样实现的:
首先,为user model添加一个类型为string的perishable_token字段,和一个类型为boolean的active字段(这样user model就有了一个active?和active=方法),并修改migration使active属性默认值为false,这样用户在注册之后就不能马上登录。
在创建好用户之后给用户的邮箱发一封带有user.perishable_token的值的邮件(可以用callback、observe来做),让用户使用这个token访问某个controller(比如UsersController)的某个action(比如active),在这个action中通过perishable_token找到对应的user对象,注意,是使用User.find_using_perishable_token而不是find_by_perxxxxxx,)我大概的写一下伪代码(不保证没有问题,只是说明一下思路):
class UsersController < ApplicationController
  def active
    @user = User.find_using_perishable_token(params[:perishable_token])
    if @user
      @user.active = true
      @user.reset_perishable_token #另外有一个带感叹号的版本。
      @user.save
      flash[:notice] = 'active successful!'
      redirect_to any_path
    else
      do some thing else
    end
  end
end

可以参考一下这篇文章: http://www.binarylogic.com/2008/11/16/tutorial-reset-passwords-with-authlogic/


另外关于OpenID的插件,先放个链接,有空回来补吧,或者另外写一篇:
http://railscasts.com/episodes/170-openid-with-authlogic
插件在这: http://github.com/binarylogic/authlogic_openid

你可能感兴趣的:(Rails,authlogic)