【问题标题】:Devise Confirmation invalid on first send首次发送时设计确认无效
【发布时间】:2014-02-05 02:39:49
【问题描述】:

我有一个使用 :confirmable 的带有 Devise 3.2.2 的 Rails 4 应用程序,但在发送无效确认令牌时遇到了问题,但只是第一次。重新发送确认令牌后,该链接有效。

相关路线:

devise_for :users, skip: [:sessions, :passwords, :confirmations, :unlocks, :registrations, :invitations]
as :user do
  ...
  # joining
  get   '/register' => 'devise/registrations#new',    as: 'new_user_registration'
  post  '/register' => 'devise/registrations#create', as: 'user_registration'
  ...
end

...以及有问题的邮件模板:

<p>Welcome <%= @email %>!</p>

<p>You can confirm your account email through the link below:</p>

<p><%= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @token) %></p>

据我所知,一切都是相当标准的票价,而且它只会在最初创建而不是重新发送时失败,这一事实相当令人困惑。

--更新--

单击该链接时,我的开发日志中会显示以下内容:

Started GET "/account/confirm?confirmation_token=3bQP-EvYJPv74s9AMz63" for 127.0.0.1 at 2014-02-07 12:26:10 -0500
Processing by Users::ConfirmationsController#show as HTML
  Parameters: {"confirmation_token"=>"3bQP-EvYJPv74s9AMz63"}
  User Load (0.4ms)  SELECT "users".* FROM "users" WHERE "users"."confirmation_token" = 'e191b48746f15014beb32073f08de3c7aa13a2282216f089fa71d083901b3dca' ORDER BY "users"."id" ASC LIMIT 1
  Rendered devise/shared/_links.slim (1.2ms)
  Rendered devise/confirmations/new.html.slim within layouts/devise (7.0ms)
  Rendered layouts/_jquery.slim (1.0ms)
Completed 200 OK in 32ms (Views: 23.4ms | ActiveRecord: 0.4ms)

引用的控制器是:

class Users::ConfirmationsController < Devise::ConfirmationsController

  protected

  def after_confirmation_path_for(resource_name, resource)
    if signed_in?
      signed_in_root_path(resource)
    else
      new_session_path(resource_name, email: resource.email)
    end
  end

end

-- 更新 2--

以下是整个注册过程的日志要点: https://gist.github.com/jbender/bbe079c2dd3fa2d1e664

【问题讨论】:

  • 根据开发日志,状态为Completed 200 OK,因此我假设此日志用于来自Users::ConfirmationsController#show 操作的重新发送确认。能否分享一下第一次生成的确认邮件token链接内容以及点击后的日志,以便我们分析数据。
  • 不,这是第一次尝试的日志(失败)。发布的日志消息会导致显示“无效令牌”错误,即使它是 200 状态。

标签: ruby-on-rails devise ruby-on-rails-4 devise-confirmable


【解决方案1】:

在注册create 操作完成之前,您的用户似乎被立即更新和重新保存(包括生成新的确认令牌)。看看你的要点:

  • 第 5 行显示设计检查具有电子邮件地址的现有用户
  • 第 6 行显示设计检查它刚刚生成的确认令牌是否已经存在于其他用户(显然他们需要是唯一的)
  • 第 9 行显示正在插入数据库的新用户记录
  • 第 11 行看起来 devise 刚刚生成了一个新的确认令牌,并且正在检查这个新令牌是否已经在使用中。
  • 然后第 14 行显示正在保存的更新用户详细信息,包括这个新的确认令牌。我注意到第 9 行的初始插入中没有一个 tos_accepted_at 字段。 unconfirmed_email 字段也已设置,confirmation_sent_at 字段比第 9 行中保存的字段晚 1 秒;这可能只是由于正常的 :confirmable 行为,尽管我很惊讶设计没有发现电子邮件地址与现有(未经确认的)电子邮件地址相同并且没有打扰的事实。

问题是为什么用户被保存了两次?看起来您添加了一个 tos_accepted_at 属性。是否有一些代码(后过滤器?)重新保存用户,包括此属性以及所有其他属性,然后再次触发设计的可确认逻辑?说到这一点,我最感兴趣的是,它并没有立即导致发送第二封电子邮件(带有有效的确认令牌),因为 Confirmation_sent_at 时间戳发生了变化。

我注意到,从其他 INSERT 来看,还有一些版本跟踪正在进行中,尽管这看起来不会造成干扰。

作为额外的健全性检查,如果您可以使用 Rails 控制台对您期望的确认令牌进行加密,您会发现加密版本与您的要点第 6 行的版本相匹配,然后在第 9 行的 db。我不能尝试这个,因为它取决于您的 rails 机密(请参阅 devise/toekn_generator.rb 中的 Devise::TokenGenerator)。无论如何,没有必要,但会确认原始确认令牌永远不会起作用。

我假设重新发送有效,因为它只是正常情况(没有双重保存,没有额外的 tos_accepted_at 字段等)

更新

正如 cmets 中所讨论的,问题确实出在 tos_accepted_at 属性上。它正在使用update_attribute()after_create 回调中进行更新。由于有点不清楚的原因,这似乎弄脏了所有属性,所以它们也都被保存了(update_attribute 也会保存所有其他属性,如果它们是脏的)并设计生成一个新的确认令牌作为此过程的一部分(虽然我们认为它不应该有,因为电子邮件地址实际上并没有改变!)。将保存更新的tos_accepted_at 的过滤器更改为before_save 过滤器而不是after_create 通过确保用户只保存一次来避免问题,因为before_save 显然发生在第一次保存之前而不是after_create这当然发生在将用户插入数据库的保存之后。

【讨论】:

  • 在创建时验证了tos_agreement,以及更新它的after_create(用户是可邀请的,因此只有在他们自己注册时才设置),但这只是一个self.update_attribute(:tos_accepted_at, Time.now) 如果满足条件则调用,我认为这不会触发整个对象的重新保存?
  • 你认为它不会,但似乎如果 AR 认为其他属性是脏的,它也会更新它们。它只会真正跳过验证 - 更多信息请参见 apidock.com/rails/ActiveRecord/Persistence/update_attribute。我不知道为什么它会认为其他属性是脏的,但也许这就是正在发生的事情。稍微下手并尝试self.update_column(:tos_accepted_at, Time.now) 而不是update_attribute 看起来会跳过所有内容,除了直接更新那个精确的列吗?这至少应该排除或确认这是罪魁祸首。
  • ...和/或尝试禁用过滤器(以及任何其他添加的过滤器)。
  • 这最终成为了问题。通过调用update_tos_agreement“after_validation”,它按预期工作。您能否更新您的答案以反映真正的原因(为什么在每次保存预接受后重新生成 confirmation_token 完全是另一个问题),我将奖励赏金?
  • 我最终将其设为before_save,因为将其设为after_validation 可防止验证错误通过。话虽如此,这是一个条件验证(ToS 可能会更新,他们需要再次接受)。
【解决方案2】:

根据路由文件,我假设您已覆盖注册控制器。
我的预感是您在您的注册#create 操作中做错了一些事情。具体来说,confirmation_token 在创建时没有正确存储在用户记录中。

我的理论是:

  1. confirmable.rb 的第 278 行 返回 false 因为第 277 行没有找到具有提供的令牌的用户,因为它没有正确存储。因此,277 创建了一个新的用户记录,而 278 返回了false

  2. 当您完成重新发送过程时,整个令牌生成/存储过程将再次完成confirmable.rbconfirmations_controller.rb 中的确认#create。这修复了registrations#create中遗漏的存储步骤。

您能否在初始创建时验证 Confirmation_token 是否正确存储在您的用户模型中?

【讨论】:

  • 1) 我没有覆盖注册控制器,只是更改了路径(他们仍然会去devise/registrations#create) 2) 用户被正确保存,并且包含预期的令牌: #&lt;User id: 12, ... confirmation_token: "86451a96685ec5619bbb191c06f7c68785b16f2f67cd50b436f...", confirmed_at: nil, confirmation_sent_at: "2014-02-07 17:25:41", unconfirmed_email: "foo@test.com" ... created_at: "2014-02-07 17:25:41", updated_at: "2014-02-07 17:25:41"&gt;
猜你喜欢
  • 1970-01-01
  • 2016-07-22
  • 2013-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多