无需密码即可更新“用户”属性
Posted
技术标签:
【中文标题】无需密码即可更新“用户”属性【英文标题】:Updating `User` attributes without requiring password 【发布时间】:2011-10-28 08:30:40 【问题描述】:现在,用户无需输入密码即可编辑他们的一些属性,因为我的验证设置如下:
validates :password, :presence =>true, :confirmation => true, :length => :within => 6..40 , :on => :create
validates :password, :confirmation => true, :length => :within => 6..40 , :on => :update, :unless => lambda |user| user.password.blank?
但是,在用户执行此操作后,他们的密码将被删除 - update_attributes 正在将他们的密码更新为“”。这是我的更新定义:
def update
if @user.update_attributes(params[:user])
flash[:success] = "Edit Successful."
redirect_to @user
else
@title = "Edit user"
render 'edit'
end
end
我也尝试过使用 update_attribute 的不同定义:
def save_ff
@user = User.find(params[:id])
@user.update_attribute(:course1, params[:user][:course1] )
@user.update_attribute(:course2, params[:user][:course2] )
@user.update_attribute(:course3, params[:user][:course3] )
@user.update_attribute(:course4, params[:user][:course4] )
redirect_to @user
end
但由于某种原因,这是在做同样的事情。如何在不更改密码的情况下更新某些用户属性?谢谢!
【问题讨论】:
【参考方案1】:我没有意识到我昨天给你的解决方案会导致这个问题。对不起。
好吧,灵感来自from devise,您应该以这种方式简单地更新您的控制器:
def update
params[:user].delete(:password) if params[:user][:password].blank?
if @user.update_attributes(params[:user])
flash[:success] = "Edit Successful."
redirect_to @user
else
@title = "Edit user"
render 'edit'
end
end
【讨论】:
哦,这很聪明。这样,您可以使用相同的更新操作并将用户的更新表单拆分到多个视图中,您只需要记住如果属性为空,则删除它。 啊,我现在感觉很糟糕。但是从 Rails 4 开始,这个答案对我不起作用,因为我们现在在 params 散列本身上使用强参数,所以这个答案对复制粘贴的初学者不起作用。这个概念仍然是合理的,所以也许它不会让人大跌眼镜。如果您使用强大的参数编辑您的答案,我将能够投票。 这是在 2011 年发布的......如果你想撤消,让我们做一个空编辑......或者在强大的参数之前责怪每个发布答案的人 听起来乔佛里国王说话不是很严厉……我带着你令人愉快的责备能力离开了。继续劝阻人们帮助他人 我正在使用强参数;将聪明的线放入我的更新功能中可以完美地工作,无需任何其他更改。很好的解决方案,谢谢。【参考方案2】:我遇到了同样的问题。我无法用
修复它params[:user].delete(:password) if params[:user][:password].blank?
我只能通过单独对每个项目执行“update_attribute”来使其工作,例如
if ( @user.update_attribute(:name, params[:user][:name]) &&
@user.update_attribute(:email, params[:user][:email]) &&
@user.update_attribute(:avatar, params[:user][:avatar]) &&
@user.update_attribute(:age, params[:user][:age]) &&
@user.update_attribute(:location, params[:user][:location]) &&
@user.update_attribute(:gender, params[:user][:gender]) &&
@user.update_attribute(:blurb, params[:user][:blurb]) )
flash[:success] = "Edit Successful."
redirect_to @user
else
@title = "Edit user info"
render 'edit'
end
这显然是一个彻头彻尾的黑客攻击,但这是我在不弄乱验证和删除密码的情况下弄清楚的唯一方法!
【讨论】:
在最坏的情况下,您的用户将被更新多次,这样做确实是个坏主意。【参考方案3】:我遇到了同样的问题,上面的解决方案对我不起作用。我找到了真正的罪魁祸首:我的用户模型中有一个 encrypt_password 回调,每次都将密码设置为空白。
before_save :encrypt_password
我通过在此回调的末尾添加一个条件来修复它:
before_save :encrypt_password, :unless => Proc.new |u| u.password.blank?
【讨论】:
这会造成任何安全漏洞吗?我想我也有一个 validates :password 应该可以处理它,但是我对 rails 的了解还不够,无法知道不加密密码是否会产生任何问题。 这与@Starkers 下面的答案相结合,为我解决了问题。谢谢!【参考方案4】:This blog post 演示了您想要做的事情的原则。
未显示但可能有用的是向模型添加访问器:
attr_accessor :new_password, :new_password_confirmation
attr_accessible :email, :new_password, :new_password_confirmation
并在用户提供新密码的情况下提供所有所需的验证。
validates :new_password, :presence => true,
:length => :within => 6..40 ,
:confirmation => true,
:if => :password_changed?
最后,我会添加一个检查来查看是否已设置 encrypted_password 以确定“password_changed?”是否已更改?为了在新记录上要求密码。
def password_changed?
!@new_password.blank? or encrypted_password.blank?
end
【讨论】:
设计正在使用similar approach【参考方案5】:# It smells
def update
if params[:user][:password].blank?
params[:user].delete :password
params[:user].delete :password_confirmation
end
if @user.update_attributes(params[:user])
flash[:success] = "Edit Successful."
redirect_to @user
else
@title = "Edit user"
render 'edit'
end
end
# Refactoring
class User < ActiveRecord::Base
...
def update_attributes(params)
if params[:password].blank?
params.delete :password
params.delete :password_confirmation
super params
end
end
...
end
def update
if @user.update_attributes(params[:user])
flash[:success] = "Edit Successful."
redirect_to @user
else
@title = "Edit user"
render 'edit'
end
end
# And little better
class User < ActiveRecord::Base
...
def custom_update_attributes(params)
if params[:password].blank?
params.delete :password
params.delete :password_confirmation
update_attributes params
end
end
...
end
def update
if @user.custom_update_attributes(params[:user])
flash[:success] = "Edit Successful."
redirect_to @user
else
@title = "Edit user"
render 'edit'
end
end
【讨论】:
绝对可能是 Rails 4 中最简单的方法,也是对@Richard W 代码的出色重构。【参考方案6】:@user.username=params[:username]
if @user.update_attribute(:email,params[:email])
flash[:notice]="successful"
else
flash[:notice]="fail"
end
以上代码可以更新用户名和电子邮件字段。 因为 update_attribute 可以更新脏字段。 但很遗憾,update_attribute 会跳过验证。
【讨论】:
【参考方案7】:我一直在努力解决这个问题并在圈子里转了一段时间,所以我想我应该把我的 Rails 4 解决方案放在这里。
到目前为止,我看到的答案都没有满足我的用例,它们似乎都涉及以某种方式绕过验证,但我希望能够验证其他字段以及密码(如果存在)。另外我没有在我的项目中使用设计,所以我不能使用任何特定的东西。
值得指出的是,这是一个两部分的问题:
第 1 步 - 如果您的控制器中的密码为空,您需要从强参数中删除密码和确认字段:
if myparams[:password].blank?
myparams.delete(:password)
myparams.delete(:password_confirmation)
end
第 2 步 - 您需要更改验证,以便在未输入密码时不验证密码。我们不希望将其设置为空白,因此我们之前将其从参数中删除。
在我的情况下,这意味着在我的模型中将其作为验证:
validates :password, :presence => true, :confirmation => true, length: minimum: 7, :if => :password
注意 :if => :password - 如果没有设置密码,则跳过检查。
【讨论】:
我喜欢这种方法,但我认为将密码验证限制为像validates :password, :presence => true, :confirmation => true, length: minimum: 7, on: :create
这样的 create 方法更安全,因此只会对 create 方法进行验证【参考方案8】:
正确答案不再适用于 rails 4。我相信我的答案是最简洁和最通用的,只要您想省略任何属性(不仅仅是密码),它就可以工作。如果您想在多个不同位置更新任何模型的单独属性,则需要这种方法。
例如,如果您想做 Stack Overflow 所做的事情并通过 security
页面更新密码,则可以通过用户显示视图更新个人资料图像,并通过用户编辑视图更新用户的大部分信息。
1) 使用类方法扩展hash class
以删除空白值。我们将使用此方法删除未更新但仍存在于 params 哈希中的空白值:
1a) 在lib
目录下的ext
目录下创建一个hash.rb
文件:
命令行
$ mkdir lib/ext
$ touch lib/ext/hash.rb
1b) 在hash.rb
内部,“创建”一个Hash
类并创建一个.delete_blanks!
方法:
lib/ext/hash.rb
class Hash
def delete_blanks!
delete_if |k, v| v.nil?
end
end
1c) 要求这个文件(和你的整个 lib 目录)进入在初始化器中引用它的 rails:
config/boot.rb
# other things such as gemfiles are required here, left out for brevity
Dir['lib/**/*.rb'].each |f| load(f) # requires all .rb files in the lib directory
2) 在 users#update 操作中,实现我们闪亮的新 delete_blanks!类方法从参数哈希中删除我们没有更新的属性。然后,通过update_attributes
方法更新用户实例,*不是update
方法!
2a) 首先,让我们使用 delete_blanks!修复我们的 user_params 哈希的方法:
app/controllers/users_controller.rb
new_params = user_params.delete_blanks!
2b) 现在让我们使用update_attributes
方法更新实例(同样,不是update
方法):
app/controllers/users_controller.rb
@user.update_attributes(new_params)
这是完成的users#update
操作的外观:
app/controllers/users_controller.rb
def update
new_params = user_params.delete_blanks!
if @user.update_attributes(new_params)
redirect_to @user, notice: 'User was successfully updated.'
else
render action: 'edit' // or whatever you want to do
end
end
3) 在User
模型中,将if: :<attribute>
选项添加到所有验证中。这是为了确保仅当参数哈希中存在该属性时才触发验证。我们的delete_blanks!
方法将从参数哈希中删除属性,因此不会运行密码验证。还值得注意的是,delete_blanks!
仅删除值为 nil 的哈希条目,而不是具有空字符串的哈希条目。因此,如果有人在用户创建表单(或任何带有密码字段的表单)上遗漏了密码,则存在验证将生效,因为哈希的 :password 条目不会为零,它将为空字符串:
3a) 在所有验证中使用if:
选项:
app/models/user.rb
VALID_EMAIL_REGEX = /[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9\-.]/
validates :first_name, presence: true, if: :first_name
validates :last_name, presence: true, if: :last_name
validates :user_name, presence: true, if: :user_name
validates :email, presence: true,
uniqueness: case_sensitive: false ,
format: with: VALID_EMAIL_REGEX , if: :email
validates :password, length: minimum: 6, maximum: 10 , if: :password
就是这样。现在,用户模型可以在整个应用程序中通过许多不同的形式进行更新。属性的存在验证仍然在任何包含它的字段的表单上发挥作用,例如密码存在验证仍然会在 user#create
视图中发挥作用。
这可能看起来比其他答案更冗长,但我相信这是最可靠的方法。您可以在无限数量的不同模型上为User
实例单独更新无限数量的属性。请记住,当您想使用新模型执行此操作时,您需要重复步骤 2a)、2b) 和 3a)
【讨论】:
我喜欢这种方法,但上面的方法起初对我不起作用。删除空白!方法没有改变哈希值,所以我不得不将更新操作中的那一行更改为 new_params = user_params.delete_blanks! @ChrisHawkins 啊好拯救!虽然我的 bang 方法确实就地改变了散列,但update_attributes(user_params)
中的 user_params
不是包含散列的变量,而是对 user_params
方法的调用,它返回一个新的、未更改的散列。我会修正我的答案:)【参考方案9】:
2017 年答案:
在 Rails 5 中,正如 Michael Hartl 的 tutorial 所指出的那样,您的模型中包含以下内容就足够了:
validates :password, presence: true, length: minimum: 6 , allow_nil: true
allow_nil: true 是这里的关键,它允许用户编辑他/她的信息而无需更改密码。
此时可能会认为这也将允许空用户注册;然而,这可以通过使用 has_secure_password
来防止,它会自动验证密码是否存在,但只能使用 create
方法。
这是一个演示用户模型,用于说明目的:
class User < ApplicationRecord
attr_accessor :remember_token
before_save self.email = email.downcase
validates :name, presence: true, length: maximum: 50
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: maximum: 255 ,
format: with: VALID_EMAIL_REGEX ,
uniqueness: case_sensitive: false
has_secure_password
validates :password, presence: true, length: minimum: 6 , allow_nil: true
.
.
.
end
我不知道如何使用设计来做到这一点。我的两分钱。
【讨论】:
以上是关于无需密码即可更新“用户”属性的主要内容,如果未能解决你的问题,请参考以下文章
无需硬编码用户名/密码即可更新 Active Directory