Rails Devise密码重置电子邮件,允许多次提交

问题描述 投票:3回答: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

有没有办法只对第一次提交做出回应?

谢谢

ruby-on-rails ruby ajax devise password-recovery
3个回答
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 *

添加:对您的回答的回复

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

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将检查此值,以确定是否应发送令牌。我尚未验证,但应该可以。 

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

我已经简化了trh的想法,该想法有选择地转发到原始的devise代码。如果在过去一小时内发送了一个请求,它会假装它已再次发送,并假设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


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

© www.soinside.com 2019 - 2024. All rights reserved.