但是,完全相同的代码在本地使用rails s命令可以正常工作。邮件发送成功,我也可以重设密码。我使用的代码只是标准的Devise代码,我没有覆盖任何内容。
也许这与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
包含reset_password_token
而不是电子邮件或用户名的情况。
编辑:补充说明:
当你说 'devise :registerable, :trackable, ...' 时,Devise会为你的模型添加功能。
在你的用户模型(或管理员等)中,你可以重写名为find_first_by_auth_conditions的Devise方法。这个特殊的方法被Devise逻辑用于定位要登录的记录。Devise会在名为warden_conditions的参数中传入一些信息。其中会包含一个电子邮件、一个用户名、一个reset_password_token,或者你添加到Devise登录表单中的任何其他信息(例如一个账户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
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
实际上是编码的=
字符。3D
的J5Z5g6QNVQb3ZXkiKjTx
。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的更新日志博客文章或devise的问题讨论中找到
如果您正在使用自定义确认邮件视图,除了@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>
@token
,必须使用@resource.reset_password_token
才能使其正常工作。Devise 4.1.1 - Francisco Quintero