一个Padrino应用基于上述答案正在执行,我已经做了一些实验,以获得期望的结果。我最终创建了一个“private”password_hash属性和一个名为password的虚拟访问器。
我在这个过程中做了一些意见:
看来,ActiveRecord的没有私人属性的任何概念。使用诸如private :password, :password=
之类的符号使私有访问器方法不是一种选择,因为Rails在实例化模型时抛出了NameError: undefined method
,因为模型本身没有定义这两种方法(它们似乎从ActiveRecord::Base
继承)。
用纯粹的东西覆盖password_hash访问器非常适合防止对属性的操作,但它也意味着ActiveRecord本身在更新password_hash属性时失败,因为它调用的是空实现。
因此,使访问器专用失败,因为它们在实际模型中未定义。定义它们也失败了,因为它打破了ActiveRecord。所以,你可以做什么?
我做了两个和更多。我使访问器是私有的,通过调用super
来定义它们并实现。这可以防止控制器(和导轨控制台)通过抛出NoMethodError
来访问它们,但不会拒绝ActiveRecord。
一个侧面说明:验证问题
一个问题,我遇到了我的做法被打破验证。在password_hash上执行最小长度是不好的,因为任何密码(甚至没有)会导致128个字符的SHA512哈希。所以验证散列没什么意义。相反,我向虚拟密码访问器添加了验证,并添加了一个回调函数,用于检查是否已设置虚拟访问器属性,如果是,则将其哈希并写入password_hash属性。
最终实现
我实现弄成这样:
class User < ActiveRecord::Base
attr_accessible :first_name, :last_name, :email
attr_accessor :password
validates :password, :length => { :minimum => 8 }, :if => :password_changed?
validates :first_name, :last_name, :email, presence: true
# Various associations
before_save :hash_password
def password_correct?(p)
if(password.present?)
password == p
else
read_attribute(:password_hash) == hash_string(p)
end
end
def role_symbols
roles.collect do |r|
r.name.to_sym
end
end
private
def hash_string(input)
Digest::SHA2.new(512).update(input).hexdigest
end
def hash_password
if(password.present?)
write_attribute(:password_hash, hash_string(password))
self.password = nil
end
end
def password_changed?
password.present? or new_record?
end
def password_hash
super
end
def password_hash=(p)
super
end
end
见接受的答案这个[问题](http://stackoverflow.com/questions/3764899/is-there-a-way-to-make-rails-activerecord-attributes-private)。 – cdesrosiers
这是什么意思?它不提供任何安全性 - 控制台用户总是可以通过SELECT语句从数据库获取属性。 – zetetic
你不应该使用'has_secure_password'选项吗? – toxaq