3

我有以下代码允许用户以 AJAX 表单请求密码重置:

<%= form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :post },:remote =>'true') do |f| %>
 <%= devise_error_messages! %>
 <div><%= f.label :email %><br />    
 <%= f.email_field :email %></div>
 <div><%= f.submit "Send me reset password instructions" %></div>
<% end %>

这允许这样的行为,即如果用户反复单击按钮,或反复按“输入”,则在服务器可以提供响应之前,将发送相应的密码重置电子邮件。

以下是在 devise/password_controller.rb 中

def create
 self.resource = resource_class.send_reset_password_instructions(resource_params)   
 if successfully_sent?(resource)
  flash[:notice] = "You will receive an email with instructions about how to reset your password in a few minutes."
  respond_to do |format|
   format.html #responds with default html file
   format.js 
  end    
 else
  respond_to do |format|
   format.html #responds with default html file
   format.js{ render :js => "$(\".deviseErrors\").html(\"<span class='login-error'>Could not send reset instructions to that address.</span>\");" } #this will be the javascript file we respond with
  end
 end
end

有没有办法只回复第一次提交?

谢谢

4

4 回答 4

4

我认为如果您正在与客户打交道,这个想法非常有用,他们不是等待电子邮件而是重新请求 3 或 4 次,此时第一个可能会出现,但现在链接无效。滞后或只是重新发送相同的链接很好,但正如我上面提到的,它不再是(?)在设计代码中,它只是处理过期的旧重置请求,而不是限制新请求的发送。

我采用了 trh 想法的简化版本,它有选择地转发到原始设计代码。如果在最后一小时内发送了一个请求,它只是假装它再次发送了它,并假设 Mailgun 或您使用的任何人都会收到它需要去的地方的消息。

class Members::PasswordsController < Devise::PasswordsController
  def create
    self.resource = resource_class.find_by_email(resource_params[:email])
    if resource && (!resource.reset_password_sent_at.nil? || Time.now > resource.reset_password_sent_at + 1.hour)
      super
    else
      flash[:notice] = I18n.t('devise.passwords.send_instructions')
      respond_with({}, location: after_sending_reset_password_instructions_path_for(resource_name))
    end
  end
end

行为如下:

  specify "asking twice sends the email once only, until 1 hour later" do
    member = make_activated_member
    ActionMailer::Base.deliveries.clear
    2.times do
      ensure_on member_dashboard_path
      click_on "Forgotten your password?"
      fill_in "Email", :with => member.email
      click_on "Send me password reset instructions"
    end
    # see for mail helpers https://github.com/bmabey/email-spec/blob/master/lib/email_spec/helpers.rb

    expect(mailbox_for(member.email).length).to eq(1)
    expect(page).to have_content(I18n.t('devise.passwords.send_instructions'))    

    Timecop.travel(Time.now + 2.hours) do
      expect {
        ensure_on member_dashboard_path
        click_on "Forgotten your password?"
        fill_in "Email", :with => member.email
        click_on "Send me password reset instructions"
      }.to change{mailbox_for(member.email).length}.by(+1)
    end
  end

更新它以重新发送具有相同链接的原始电子邮件的奖励积分,如在此测试中:

  specify "asking twice sends the same link both times" do
    member = make_activated_member
    ActionMailer::Base.deliveries.clear
    2.times do
      visit member_dashboard_path
      click_on "Forgotten your password?"
      fill_in "Email", :with => member.email
      click_on "Send me password reset instructions"
    end
    # see for mail helpers https://github.com/bmabey/email-spec/blob/master/lib/email_spec/helpers.rb

    mails = mailbox_for(member.email)
    expect(mails.length).to eq(2)
    first_mail = mails.first
    second_mail = mails.last

    expect(links_in_email(first_mail)).to eq(links_in_email(second_mail))
  end
于 2014-08-16T01:42:40.710 回答
3

我建议使用 JavaScript 来防止多次提交。

$('form#reset_password').on('submit', function() {
  $(this).find('input[type="submit"]').attr('disabled', 'disabled')
})

这会将提交按钮设置为“禁用”状态,用户无法再次提交。

关于表单的禁用属性参考:http ://www.w3schools.com/tags/att_input_disabled.asp *

加:回复thr的回答

我浏览了设计源,发现应该有模型级别的解决方案。要设置每个重置请求之间允许的最大间隔,请在资源模型中添加

class User < ActiveRecord::Base

  def self.reset_password_with
    1.day
    # Determine the interval. Any time objects will do, say 1.hour
  end
end

然后 Devise::Models::Recoverable 将检查这个值来决定是否应该发送一个令牌。我还没有验证这一点,但它应该可以工作。

于 2013-09-11T16:03:15.370 回答
1

如果您真的只是想阻止人们双击提交,那么正如 billy-chan 在他的回答中所建议的那样,通过 javascript 进行限制是可行的方法。

如果您想限制发送请求到给定用途之间的时间量,那么您可以设置资源并将该功能包装在 if 语句中,检查发送最后一个密码请求时的时间戳。像这样的东西

def create
  self.resource = resource_class.find_by_email(resource_params[:email])
  if resource.reset_password_sent_at.nil?  ||  Time.now > resource.reset_password_sent_at + 5.minutes
    self.resource = resource_class.send_reset_password_instructions(resource_params)
    if successfully_sent?(resource)
      flash[:notice] = "You will receive an email with instructions about how to reset your password in a few minutes."
      respond_to do |format|
        format.html #responds with default html file
        format.js
      end
    else
      respond_to do |format|
        format.html #responds with default html file
        format.js{ render :js => "$(\".deviseErrors\").html(\"<span class='login-error'>Could not send reset instructions to that address.</span>\");" } #this will be the javascript file we respond with
      end
    end
  else
    flash[:error] = "Passwords can only be reset every 5 minutes."
    respond_to do |format|
      format.html #responds with default html file
      format.js
    end
  end
end
于 2013-09-11T18:18:49.273 回答
1

你可以在设计中做这样的事情:

class User < ActiveRecord::Base
  def send_reset_password_instructions
    super unless reset_password_sent_at.present? && reset_password_sent_at > DateTime.now - 1.day
  end
end

1.day允许的密码重置之间的间隔在哪里。

于 2020-07-10T11:14:12.483 回答