我有以下代码,允许用户以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
有没有办法只对第一次提交做出回应?
谢谢
$('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将检查此值,以确定是否应发送令牌。我尚未验证,但应该可以。
我已经简化了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
如果要限制发送请求到给定用途之间的时间,则可以设置资源并将该功能包装在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