首次发送时设计确认无效

我有一个Rails 4应用程序与Devise 3.2.2使用:confirmable ,并遇到一个问题,它发送无效的确认令牌,但只是第一次 。 重新发送确认令牌后,链接将起作用。

相关路线:

 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 

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

 

Welcome !

You can confirm your account email through the link below:

@token) %>

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

–Update–

单击链接时,我在开发日志中获得以下内容:

 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

在注册create操作完成之前,看起来有些东西会导致您的用户立即更新并重新保存(包括生成新的确认令牌)。 看着你的要点:

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

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

我注意到有一些版本跟踪正在进行,从其他INSERT判断,虽然它看起来不会干扰。

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

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

UPDATE

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

根据路径文件,我假设你已经覆盖了注册控制器。
我的预感是你在注册#create action中做了一些不正确的事情。 具体来说,在创建用户记录时,confirm_token未正确存储在用户记录中。

我的理论是:

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

  2. 当您完成重新发送过程时,整个令牌生成/存储过程将再次完成确认.rb并在confirmmations_controller.rb中确认#create 。 这修复了注册#create中错过的存储步骤。

您可以validation在初始创建时,您的用户模型是否正确存储了confirmation_token?