我正在尝试将一个现有的管理员模型转换为Devise。我们已经有了一个密码哈希,但显然不符合Devise的要求。我想要做的是接受登录表单并检查提供的密码与加密密码是否一致。如果不正确,则使用旧的哈希来检查密码,如果匹配,则清空旧的password_hash字段,并将Devise的密码设置为提供的密码并保存模型。
如何最好地前进?我怀疑我需要覆盖某些东西,也许在自定义控制器中,但我不确定如何继续下去。
如何最好地前进?我怀疑我需要覆盖某些东西,也许在自定义控制器中,但我不确定如何继续下去。
您可以让Devise使用新的加密方案对密码进行加密,就像在https://gist.github.com/1704632中所示的那样:
class User < ActiveRecord::Base
alias :devise_valid_password? :valid_password?
def valid_password?(password)
begin
super(password)
rescue BCrypt::Errors::InvalidHash
return false unless Digest::SHA1.hexdigest(password) == encrypted_password
logger.info "User #{email} is using the old password hashing method, updating attribute."
self.password = password
true
end
end
end
# Because we have some old legacy users in the database, we need to override Devises method for checking if a password is valid.
# We first ask Devise if the password is valid, and if it throws an InvalidHash exception, we know that we're dealing with a
# legacy user, so we check the password against the SHA1 algorithm that was used to hash the password in the old database.
alias :devise_valid_password? :valid_password?
def valid_password?(password)
begin
devise_valid_password?(password)
rescue BCrypt::Errors::InvalidHash
Digest::SHA1.hexdigest(password) == encrypted_password
end
end
正如您所看到的,当devise遇到无效哈希时,它会抛出InvalidHash异常,这在验证传统用户时会发生。
我使用这个方法来回退到用于创建原始传统哈希的哈希算法。
但是它并不会更改密码,如果需要,可以简单地将其添加到该方法中。
首先,您需要将password_salt和encrypted_password复制到新的对象模型中。
我这样做是因为我需要将我的数据库用户导出到另一个应用程序,并且旧应用程序使用devise 1.0.x,而新应用程序使用2.1.x。
Class User < ActiveRecord::Base
alias :devise_valid_password? :valid_password?
def valid_password?(password)
begin
devise_valid_password?(password)
rescue BCrypt::Errors::InvalidHash
salt = password_salt
digest = nil
10.times { digest = ::Digest::SHA1.hexdigest('--' << [salt, digest, password, nil].flatten.join('--') << '--') }
digest
return false unless digest == encrypted_password
logger.info "User #{email} is using the old password hashing method, updating attribute."
self.password = password
self.password_salt = nil # With this you will knew what object already using the new authentication by devise
self.save
true
end
end
end
def valid_password?(password)
if has_legacy_password?
return false unless valid_legacy_password?(password)
convert_legacy_password!(password)
true
else
super(password)
end
end
protected
def has_legacy_password?
password_salt.present?
end
def convert_legacy_password!(password)
self.password = password
self.password_salt = nil
self.save
end
def valid_legacy_password?(password)
stretches = 10
salt = password_salt
pepper = nil
digest = pepper
stretches.times do
tokens = [salt, digest, password, pepper]
digest = Digest::SHA512.hexdigest('--' << tokens.flatten.join('--') << '--')
end
Devise.secure_compare(encrypted_password, digest)
end
请务必用你用来加密密码的值替换stretches
和pepper
。
按照Thomas Dippel的指示,我已经创建了一个Gist来更新密码: https://gist.github.com/1578362
# Because we have some old legacy users in the database, we need to override Devises method for checking if a password is valid.
# We first ask Devise if the password is valid, and if it throws an InvalidHash exception, we know that we're dealing with a
# legacy user, so we check the password against the SHA1 algorithm that was used to hash the password in the old database.
#SOURCES OF SOLUTION:
# https://dev59.com/xm025IYBdhLWcg3wRDq8
# https://github.com/binarylogic/authlogic/blob/master/lib/authlogic/crypto_providers/sha512.rb
# https://github.com/plataformatec/devise/blob/master/lib/devise/encryptors/authlogic_sha512.rb
alias :devise_valid_password? :valid_password?
def valid_password?(password)
debugger
begin
devise_valid_password?(password)
rescue BCrypt::Errors::InvalidHash
stretches = 20
digest = [password, self.password_salt].flatten.join('')
stretches.times {digest = Digest::SHA512.hexdigest(digest)}
if digest == self.encrypted_password
#Here update old Authlogic SHA512 Password with new Devise ByCrypt password
# SOURCE: https://github.com/plataformatec/devise/blob/master/lib/devise/models/database_authenticatable.rb
# Digests the password using bcrypt.
# Default strategy for Devise is BCrypt
# def password_digest(password)
# ::BCrypt::Password.create("#{password}#{self.class.pepper}", :cost => self.class.stretches).to_s
# end
self.encrypted_password = self.password_digest(password)
self.save
return true
else
# If not BCryt password and not old Authlogic SHA512 password Dosn't my user
return false
end
end
end
encrypted_password
是在哪里定义的? - Andrés Gutiérrezencrypted_password
方法。 - moeffju
digest = ""
10.times { digest = Digest::SHA1.hexdigest("--" << [password_salt, digest, password, nil].flatten.join("--") << "--") }
return false unless digest == encrypted_password
- Ola Tuvessonhas_secure_password
,请将Digest::SHA1.hexdigest(password)
更改为BCrypt::Password.new(password)
。 - Rafael Oliveira