1

我有一个基本的身份验证系统,就像 Michael Hartl 的Ruby on Rails 教程中的一样。基本上,记住令牌存储在 cookie 中。我从 Railscast #124 实现了 Ryan Bate 的Beta-Invitations,您可以在其中发送有限数量的邀请。这样做时,我遇到了当前用户在发送邀请后退出的问题。这是由邀请模型中的代码引起的:

邀请.rb

belongs_to :sender, :class_name => 'User'
[...]
before_create :decrement_sender_count, :if => :sender
[...]
def decrement_sender_count
  sender.decrement! :invitation_limit
end

在日志中我看到了 sender.decrement!不仅更新了invitation_limit,还更新了remember_token:

UPDATE "users" SET "invitation_limit" = 9982, "remember_token" = 'PYEWo_om0iaMjwltU4iRBg', "updated_at" = '2012-07-06 09:57:43.354922' WHERE "users"."id" = 1

我发现了一个丑陋的解决方法,但我很想知道问题到底是什么。由于我不知道从哪里开始,我将向您展示来自用户控制器的更新方法。还有什么相关的?

users_controller.rb

def update
  @user = User.find(params[:id])
  if @user.update_attributes(params[:user])
    flash[:success] = t('success.profile_save')
    sign_in @user
    redirect_to @user
  else
    flash.now[:error] = t('error.profile_save')
    render 'edit'
  end
end
4

2 回答 2

2

在我看来,您在 ActiveRecord 更新方法中遇到了一个常见的陷阱:

在这里查看 ActiveRecord 文档,您可以看到减量的实际实现!方法:

def decrement!(attribute, by = 1)
  decrement(attribute, by).update_attribute(attribute, self[attribute])
end

有趣的部分是在 self 对象上调用的 update_attribute - 虽然该方法暗示 ActiveRecord 只会更新指定的属性,但它确实更新了 self 对象的所有脏属性。

这意味着,如果您在任何时候更改了对象的记住令牌属性,它将在 update_attributes 调用期间保存到数据库中。

如果我是对的,那就是问题所在 - 只需确保在运行时没有对 remember_token 属性进行任何更改。

除此之外,您可以考虑使用ActiveRecord 的 update_column方法,该方法将更新数据库上的单个列,而无需对对象执行保存方法

于 2012-07-06T10:21:05.223 回答
2

decrement!调用save当然会触发保存回调。看起来这本书指导你做

before_save :create_remember_token
def create_remember_token
  self.remember_token = SecureRandom.urlsafe_base64
end

这意味着保存用户将始终使记住令牌无效。我认为这是因为当用户更改密码时,记住令牌也会更改,但这意味着显然会有一些附带损害。

你可以使用decrement_counterwhich 本质上是

update users set counter_name = counter_name - 1 where id =12345

不运行任何回调。这也避免了一些竞争条件场景。但是,每当用户更改时更改令牌必然会在您不期望的时候更改令牌 - 您可能只想在相关时更改它(也许当凭据已更改时)

于 2012-07-06T10:30:00.833 回答