乐者为王

Do one thing, and do it well.

用Rails 2.3打造简单记账应用(7)

经过这几天的不断完善,记账应用越来越让人心情愉快了。不过要是能给应用加上忘记密码的功能就更好了。

创建迁移任务,在users数据表中加入一个字段用来储存修改密码时的重置码:

1
script/generate migration add_password_reset_code_to_users

执行上面的命令后就可以在db/migrate目录下看到如下格式的迁移文件:

1
20091214171949_add_password_reset_code_to_users.rb

打开它,将它的内容改为:

1
2
3
4
5
6
7
8
9
class AddPasswordResetCodeToUsers < ActiveRecord::Migration
  def self.up
    add_column :users, :password_reset_code, :string, :limit => 40
  end

  def self.down
    remove_column :users, :password_reset_code
  end
end

执行迁移任务更新数据表users:

1
rake db:migrate

然后打开config/routes.rb文件,在里面增加两个映射:

1
2
map.forgot_password '/forgot_password', :controller => 'users', :action => 'forgot_password'
map.reset_password '/reset_password/:reset_code', :controller => 'users', :action => 'reset_password'

另外还要创建两个视图文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# app/views/users/forgot_password.html.erb
<h1>Password Reset Request</h3>

<p>
  Enter your email address that we have on our file and click send.<br />
  We will send you a password reset link email to your email address.
</p>

<% form_for :user, :url => { :action => 'forgot_password' } do |f| %>
  <%= f.error_messages %>

  <p>
    <%= f.label :email %><br />
    <%= f.text_field :email %>
  </p>
  <p>
    <%= f.submit 'Send' %>
  </p>
<% end %>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# app/views/users/reset_password.html.erb
<h1>Reset Password</h1>

<% form_for :user, :url => { :action => 'reset_password' } do |f| %>
  <%= f.error_messages %>

  <p>
    <%= f.label :password %><br />
    <%= f.password_field :password %>
  </p>
  <p>
    <%= f.label :password_confirmation 'Confirm password' %><br />
    <%= f.password_field :password_confirmation %>
  </p>
  <p>
    <%= f.submit 'Reset password' %>
  </p>
<% end %>

然后就是在app/views/sessions/new.html.erb的最下面加上:

1
<%= link_to 'Forgot password?', forgot_password_path %>

在app/controllers/users_controller.rb中添加以下方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
def forgot_password
  return unless request.post?
  if @user = User.find_by_email(params[:user][:email])
    @user.forgot_password
    @user.save
    flash[:notice] = "A password reset link has been sent to your email address"
    redirect_back_or_default('/')
  else
    flash[:alert] = "Could not find a user with that email address"
  end
end

def reset_password
  @user = User.find_by_password_reset_code(params[:reset_code])
  return if @user unless params[:user]

  new_password = params[:user][:password]
  new_password_confirmation = params[:user][:password_confirmation]

  if (new_password && new_password_confirmation &&
      !new_password.blank? && !new_password_confirmation.blank?)
    @user.reset_password(new_password, new_password_confirmation)
    if @user.save
      flash[:notice] = "Password reset success."
      redirect_back_or_default('/')
    else
      flash[:error] = "Password reset failed."
    end
  else
    flash[:error] = "Password mismatch"
  end
end

再创建两个邮件模板:

1
2
3
4
5
# app/views/user_mailer/forgot_password.html.erb
Dear <%=h @user.login %>,

  We have had a request to reset your password, please visit<br />
  <%=h @url %>
1
2
3
4
5
# app/views/user_mailer/reset_password.html.erb
Your have reset the password for your account successfully.

  Username: <%=h @user.login %>
  Password: <%=h @user.password %>

下一步是打开app/models/user.rb,在它底部protected关键字的后面添加下面的代码:

1
2
3
def make_password_reset_code
  self.password_reset_code = Digest::SHA1.hexdigest(Time.now.to_s.split(//).sort_by {rand}.join)
end

然后在protected关键字的上面添加如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def forgot_password
  @forgotten_password = true
  self.make_password_reset_code
end

def reset_password(new_password, new_password_confirmation)
  # First update the password_reset_code before setting the
  # reset_password flag to avoid duplicate email notifications.
  self.password_reset_code = nil
  self.password = new_password
  self.password_confirmation = new_password_confirmation
  @reset_password = true
end

def recently_forgot_password?
  @forgotten_password
end

def recently_reset_password?
  @reset_password
end

接着来配置mailer发送通知邮件,打开app/models/user_mailer.rb加上:

1
2
3
4
5
6
7
8
9
10
def forgot_password(user)
  setup_email(user)
  @subject    += 'You have requested to change your password'
  @body[:url]  = "http://#{HOST}/reset_password/#{user.password_reset_code}"
end

def reset_password(user)
  setup_email(user)
  @subject    += 'Your password has been reset.'
end

最后打开app/models/user_observer.rb修改after_save方法为:

1
2
3
4
5
def after_save(user)
  UserMailer.deliver_activation(user) if user.recently_activated?
  UserMailer.deliver_forgot_password(user) if user.recently_forgot_password?
  UserMailer.deliver_reset_password(user) if user.recently_reset_password?
end

代码下载:https://github.com/dohkoos/qianbao

Comments