我正在使用rails 3应用程序并且已经实现了设计。我有它工作,现在我希望扩展它,以便用户不能多次使用旧密码。在github上找到了这个功能,这对我的惊喜很好。 Disallow previously passwords - Git Hub
我认为这很直接,但显然不是。我的代码如下所示:
create_passwrod_histories.rb
class CreatePasswordHistories < ActiveRecord::Migration
def self.up
create_table(:password_histories) do |t|
t.integer :user_id
t.string :encrypted_password
t.timestamps
end
end
def self.down
drop_table :password_histories
end
end
User.rb
class User < ActiveRecord::Base
include ActiveModel::Validations
has_many :roles_users
has_many :roles, :through => :roles_users
has_many :projects
has_many :password_histories
after_save :store_digest
# authorization include this in whichever model that will use ACL9
acts_as_authorization_subject
def has_role?(role_name, object=nil)
!! if object.nil?
self.roles.find_by_name(role_name.to_s) ||
self.roles.member?(get_role(role_name, nil))
else
method = "is_#{role_name.to_s}?".to_sym
object.respond_to?(method) && object.send(method, self)
end
end
def login(user)
post_via_redirect user_session_path, 'user[username]' => user.username, 'user[password]' => user.password
end
# Include default devise modules. Others available are:
# :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable #:registerable,
devise :database_authenticatable, :recoverable, :rememberable, :trackable, :validatable, :timeoutable
acts_as_authorization_subject :association_name => :roles
attr_accessor :login
# Setup accessible (or protected) attributes for your model
attr_accessible :id, :login, :username, :full_name, :email, :password, :password_confirmation, :remember_me, :role_ids
email_regex = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
validates_presence_of :username, :full_name
validates_format_of :username, :with => /^[-\w\._@]+$/i, :allow_blank => true, :message => "should only contain letters, numbers, or . - _ @"
validates_length_of :username, :minimum => 1, :allow_blank => true
validates_uniqueness_of :username, :email
validates :email, :presence => true,
:format => { :with => email_regex }
validates :password, :unique_password => true
def self.find_for_database_authentication(warden_conditions)
conditions = warden_conditions.dup
login = conditions.delete(:login)
where(conditions).where(["lower(username) = :value OR lower(email) = :value", { :value => login.downcase }]).first
end
private
def store_digest
if encrypted_password_changed?
PasswordHistory.create(:user => self, :encrypted_password => encrypted_password)
end
end
end
unique_password_validator.rb
require 'bcrypt'
class UniquePasswordValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
record.password_histories.each do |ph|
bcrypt = ::BCrypt::Password.new(ph.encrypted_password)
hashed_value = ::BCrypt::Engine.hash_secret([value, Devise.pepper].join, bcrypt.salt)
record.errors[attribute] << "has been used previously." and return if hashed_value == ph.encrypted_password
end
end
end
然后我运行我的应用并尝试使用相同的密码。然后它会抛出以下错误uninitialized constant User::PasswordHistory
答案 0 :(得分:2)
我可以从代码中看到为什么会发生这种情况的唯一方法是,如果您没有PasswordHistory
模型对象。来自Github的代码实际上没有明确告诉你这样做,但你肯定需要它。所以,也许您创建并运行了迁移但忘记创建模型,如:
class PasswordHistory < ActiveRecord::Base
...
end