我有一个基本的身份验证系统,就像Michael Hartl的Ruby on Rails Tutorial一样。基本上,记忆令牌存储在cookie中。我从Railscast#124实现了Ryan Bate的Beta-Invitations,您可以在其中发送有限数量的邀请。在这样做时,我遇到了the current user got logged out after sending an invitation的问题。这是由邀请模型中的此代码引起的:
invitation.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
我找到了一个ugly workaround,但我很想知道问题究竟是什么。由于我不知道从哪里开始,我将向用户控制器显示更新方法。还有什么可能相关?
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
答案 0 :(得分:2)
在我看来,您在ActiveRecord更新方法中遇到了常见的陷阱:
查看ActiveRecord文档here,您可以看到减量的实际实现!方法:
def decrement!(attribute, by = 1)
decrement(attribute, by).update_attribute(attribute, self[attribute])
end
有趣的部分是在自身对象上调用的update_attribute - 虽然方法暗示ActiveRecord只会更新指定的属性,但它确实更新了自身对象的所有脏属性。
这意味着,如果在任何时候更改了对象的记忆标记属性,它将在update_attributes调用期间保存到数据库中。
如果我是对的,那就是问题 - 只需确保在运行时没有对remember_token属性进行任何更改。
除此之外,您可以考虑使用ActiveRecord's update_column方法更新数据库中的单个列,而不对对象执行保存方法
答案 1 :(得分:2)
decrement!
调用save
当然会触发保存回调。看起来这本书指导你做
before_save :create_remember_token
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
这意味着保存用户将始终使记忆令牌无效。我认为这是因为当用户更改密码时,记忆标记也会改变,但这意味着显然存在一些附带损害。
你可以使用decrement_counter
本质上做
update users set counter_name = counter_name - 1 where id =12345
没有运行任何回调。这也避免了一些竞争条件。但是,每当用户更改时更改令牌必然会在您不期望的时候更改令牌 - 您可能只想在相关时更改令牌(可能在凭据更改时)