我有以下代码,允许用户以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
有没有办法只回复第一次提交?
由于
答案 0 :(得分: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的回答
我浏览了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将检查此值以决定是否应发送令牌。我没有验证这个,但它应该工作。
答案 1 :(得分:3)
我认为,如果您与客户打交道,这个想法非常有用,而不是等待电子邮件重新请求3到4次,此时第一次可能会出现,但到现在为止有一个无效的链接。滞后或只是重新发送相同的链接是很好的,但正如我上面提到的,它不再是({3}}中的{?},它只处理过期的旧重置请求,而不是限制发送新的。
我已经选择了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
答案 2 :(得分:1)
如果你真的只是试图让人们不要双击提交,那么通过javascript限制就像Billy-chan在他的回答中建议的那样。
如果要限制将请求发送到给定用途之间的时间量,则可以设置资源并将该功能包装在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
答案 3 :(得分:1)
您可以在Devise中执行以下操作:
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
是允许的两次密码重置之间的时间间隔。