我有一个 Rails 4 应用程序设置为使用 Devise,但我遇到了密码重置问题。我已经设置了邮件程序,并且密码重置电子邮件发送正常。提供的链接分配了正确的 reset_password_token,我使用该数据库进行了检查。但是,当我使用格式正确的密码提交表单时,它会提示重置令牌无效。
但是,完全相同的代码在本地通过rails s
. 电子邮件发送,我实际上可以重置密码。我使用的代码只是标准的设计代码,我没有重写任何代码。
也许它与Apache有关?我对它不太熟悉。有没有人有任何想法?
我有一个 Rails 4 应用程序设置为使用 Devise,但我遇到了密码重置问题。我已经设置了邮件程序,并且密码重置电子邮件发送正常。提供的链接分配了正确的 reset_password_token,我使用该数据库进行了检查。但是,当我使用格式正确的密码提交表单时,它会提示重置令牌无效。
但是,完全相同的代码在本地通过rails s
. 电子邮件发送,我实际上可以重置密码。我使用的代码只是标准的设计代码,我没有重写任何代码。
也许它与Apache有关?我对它不太熟悉。有没有人有任何想法?
检查代码app/views/devise/mailer/reset_password_instructions.html.erb
该链接应使用以下内容生成:
edit_password_url(@resource, :reset_password_token => @token)
如果您的视图仍然使用此代码,那将是问题的原因:
edit_password_url(@resource, :reset_password_token => @resource.password_reset_token)
Devise 开始存储令牌的哈希值,因此电子邮件需要使用真实令牌 ( @token
) 而不是存储在数据库中的哈希值创建链接。
此更改发生在 Devise 的143794d701
除了 doctororange 的修复之外,如果您要覆盖resource.find_first_by_auth_conditions
,您需要考虑warden_conditions
包含 areset_password_token
而不是电子邮件或用户名的情况。
编辑:详细说明:
当您说“设计:可注册,:可跟踪,...”时,设计会向您的模型添加功能。
在您的用户模型(或管理员等)中,您可以覆盖名为 find_first_by_auth_conditions 的设计方法。设计逻辑使用此特殊方法来定位尝试登录的记录。设计在一个名为warden_conditions 的参数中传递一些信息。这将包含电子邮件、用户名或 reset_password_token 或您添加到设计登录表单的任何其他内容(例如帐户 ID)。
例如,您可能有如下所示的内容:
(app/models/user.rb)
class User
...
def self.find_first_by_auth_conditions warden_conditions
conditions = warden_conditions.dup
if (email = conditions.delete(:email)).present?
where(email: email.downcase).first
end
end
end
但是,上面的代码会破坏密码重置功能,因为设计使用令牌来定位记录。用户不输入电子邮件,他们通过 URL 中的查询字符串输入令牌,该查询字符串被传递给此方法以尝试查找记录。
因此,当您覆盖此特殊方法时,您需要使其更加健壮以解决密码重置情况:
(app/models/user.rb)
class User
...
def self.find_first_by_auth_conditions warden_conditions
conditions = warden_conditions.dup
if (email = conditions.delete(:email)).present?
where(email: email.downcase).first
elsif conditions.has_key?(:reset_password_token)
where(reset_password_token: conditions[:reset_password_token]).first
end
end
end
如果您从日志中获取 URL,它可能如下所示:
web_1 | <p><a href=3D"http://localhost:3000/admin/password/edit?reset_password_to=
web_1 | ken=3DJ5Z5g6QNVQb3ZXkiKjTx">Change password</a></p>
在这种情况下,3DJ5Z5g6QNVQb3ZXkiKjTx
用作令牌将不起作用,因为=3D
它实际上是一个=
字符编码。
在这种情况下,您需要使用J5Z5g6QNVQb3ZXkiKjTx
(with 3D
removed)
如果您使用的是自定义确认邮件视图,则可能还值得注意(除了@doctororange 的 post ablve)以下内容。
视图中的链接在这里也发生了变化。这是新的链接代码:
<p><%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %></p>
这是旧链接代码:
<p><%= link_to 'Confirm my account', user_confirmation_url(@resource, :confirmation_token => @resource.confirmation_token) %></p>
尽管接受的答案是正确的,但想解释为什么会发生这种情况,以便您也可以在其他一些情况下使用它。如果您查看生成密码重置令牌的方法:
def set_reset_password_token
raw, enc = Devise.token_generator.generate(self.class, :reset_password_token)
self.reset_password_token = enc
self.reset_password_sent_at = Time.now.utc
self.save(validate: false)
raw
end
您将看到raw
正在返回,并且enc
正在保存在数据库中。如果您使用数据库中的值 -enc
放入password_reset_token
表单的隐藏字段中,那么它总是会说Token invalid
这是加密令牌。您应该使用的是raw
令牌。
这样做是因为万一某些管理员(或黑客)可以访问数据库,管理员可以通过使用加密令牌轻松重置任何人的密码,这是试图避免的。