Rails 4 + 设计:密码重置总是在生产服务器上给出“令牌无效”错误,但在本地工作正常。
Posted
技术标签:
【中文标题】Rails 4 + 设计:密码重置总是在生产服务器上给出“令牌无效”错误,但在本地工作正常。【英文标题】:Rails 4 + Devise: Password Reset is always giving a "Token is invalid" error on the production server, but works fine locally. 【发布时间】:2013-10-01 20:32:39 【问题描述】:我有一个设置为使用 Devise 的 Rails 4 应用程序,但我遇到了密码重置问题。我已经设置了邮件程序,并且密码重置电子邮件发送正常。提供的链接分配了正确的 reset_password_token,我使用该数据库进行了检查。但是,当我使用格式正确的密码提交表单时,它会提示重置令牌无效。
但是,完全相同的代码通过rails s
在本地运行良好。电子邮件发送,我实际上可以重置密码。我使用的代码只是标准的设计代码,我没有覆盖任何一个。
也许是 Apache 的问题?我对它不太熟悉。有没有人有任何想法?
【问题讨论】:
【参考方案1】:查看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
) 而不是存储在数据库中的哈希值创建链接。
此更改发生在 143794d701 中的设计中
【讨论】:
我也遇到了同样的问题,并且在将edit_password_url更改为使用@token后仍然会发生。知道还有什么可能导致这种情况吗?谢谢! 做到了!万分感谢! @Sakin 不确定那里发生了什么-您是否检查过自己以确保令牌相同? 非常感谢。在那里浪费了将近一个小时。 如果您使用自定义设计邮件程序,然后升级设计,也会发生这种情况,因此邮件程序 erb 文件的“新”版本将写入默认位置 救命答案。非常感谢老兄,但不知道为什么我需要使用旧版本来解决 mi 问题。我有@token
并且必须使用@resource.reset_password_token
才能让它工作。设计 4.1.1【参考方案2】:
除了doctororange 的修复之外,如果您要覆盖resource.find_first_by_auth_conditions
,您需要考虑warden_conditions
包含reset_password_token
而不是电子邮件或用户名的情况。
编辑:详细说明:
当您说“devise :registerable, :trackable, ...”时,Devise 会为您的模型添加功能。
在您的用户模型(或管理员等)中,您可以覆盖名为 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
【讨论】:
你能详细说明一下吗?我应该在我的用户模型中检查哪里? 你是救生员!整晚都在想弄清楚为什么会这样。谢谢!【参考方案3】:如果您从日志中获取 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
(去掉3D
)
【讨论】:
你是个天才!【参考方案4】:如果您使用的是自定义确认邮件视图,还可能需要注意以下内容(除了@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>
【讨论】:
【参考方案5】:虽然接受的答案是正确的,但想解释为什么会发生这种情况,以便您也可以在其他一些情况下使用它。 如果您查看生成密码重置令牌的方法:
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's change-log blog post 或devise's issue discussion 中找到有关此方面的一些信息以及 Devise 中的一些其他更改
【讨论】:
以上是关于Rails 4 + 设计:密码重置总是在生产服务器上给出“令牌无效”错误,但在本地工作正常。的主要内容,如果未能解决你的问题,请参考以下文章