52

我有一个 Rails 4 应用程序设置为使用 Devise,但我遇到了密码重置问题。我已经设置了邮件程序,并且密码重置电子邮件发送正常。提供的链接分配了正确的 reset_password_token,我使用该数据库进行了检查。但是,当我使用格式正确的密码提交表单时,它会提示重置令牌无效。

但是,完全相同的代码在本地通过rails s. 电子邮件发送,我实际上可以重置密码。我使用的代码只是标准的设计代码,我没有重写任何代码。

也许它与Apache有关?我对它不太熟悉。有没有人有任何想法?

4

5 回答 5

131

检查代码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

于 2013-10-07T23:10:28.397 回答
11

除了 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
于 2014-09-15T22:32:23.953 回答
6

如果您从日志中获取 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 3Dremoved)

于 2017-09-22T13:09:15.790 回答
0

如果您使用的是自定义确认邮件视图,则可能还值得注意(除了@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>
于 2016-07-11T15:45:00.230 回答
0

尽管接受的答案是正确的,但想解释为什么会发生这种情况,以便您也可以在其他一些情况下使用它。如果您查看生成密码重置令牌的方法:

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令牌。

这样做是因为万一某些管理员(或黑客)可以访问数据库,管理员可以通过使用加密令牌轻松重置任何人的密码,这是试图避免的。

可以在设计的更改日志博客文章设计的问题讨论中找到有关此和设计中的其他一些更改的一些信息

于 2019-06-13T07:43:06.217 回答