Rails 4 + Devise:密码重置始终在生产服务器上给出“令牌无效”错误,但在本地运行良好。

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

但是,完全相同的代码可以通过rails s在本地正常工作。电子邮件发送成功,我实际上可以重设密码。 我使用的代码只是标准的Devise代码,我没有覆盖任何代码。

也许与Apache有关? 我不太熟悉。 有人有什么想法吗?

justindao asked 2020-02-09T05:56:25Z
5个解决方案
126 votes

检查@token中的代码

该链接应通过以下方式生成:

@token

如果您的视图仍使用此代码,则将成为问题的原因:

@token

Devise开始存储令牌的哈希,因此电子邮件需要使用真实令牌(@token)而不是存储在数据库中的哈希值来创建链接。

此更改发生在143794d701中的Devise中

doctororange answered 2020-02-09T05:57:05Z
11 votes

除了Doctororange的修复程序之外,如果要覆盖resource.find_first_by_auth_conditions,则需要考虑warden_conditions包含reset_password_token而不是电子邮件或用户名的情况。

编辑:详细说明:

当您说“ devise:registerable,:trackable,...”时,Devise将功能添加到模型中。

在用户模型(或管理员等)中,您可以覆盖名为find_first_by_auth_conditions的Devise方法。 Devise逻辑使用此特殊方法来定位尝试登录的记录。 Devise在名为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

但是,上述代码将破坏密码重置功能,因为devise正在使用令牌来定位记录。 用户不输入电子邮件,而是通过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
MaximusDominus answered 2020-02-09T05:57:53Z
5 votes

如果您从日志中获取URL,则它可能如下所示:

web_1      | <p><a href=3D"http://localhost:3000/admin/password/edit?reset_password_to=
web_1      | ken=3DJ5Z5g6QNVQb3ZXkiKjTx">Change password</a></p>

在这种情况下,使用J5Z5g6QNVQb3ZXkiKjTx作为令牌将不起作用,因为3D实际上是=字符编码。

在这种情况下,您需要使用J5Z5g6QNVQb3ZXkiKjTx(已删除3D

ybart answered 2020-02-09T05:58:23Z
0 votes

如果您使用的是自定义确认邮件视图,那么除了上面的@doctororange的帖子外,还可能值得注意。

视图中的链接在这里也已更改。 这是新的链接代码:

<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>
pixelearth answered 2020-02-09T05:58:52Z
0 votes

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

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

这样做是因为在某些管理员(或黑客)可以访问数据库的情况下,管理员可以仅通过使用加密令牌轻松地重置任何人的密码,因此可以避免这种情况。

有关此更改以及Devise中其他更改的一些信息,可以在devise的change-log博客文章中或devise的问题讨论中找到。

Aleks answered 2020-02-09T05:59:27Z
translate from https://stackoverflow.com:/questions/19018657/rails-4-devise-password-reset-is-always-giving-a-token-is-invalid-error-on