2011-07-28 61 views
3

我开始研究一个已经有很多代码的项目。这是一个使用Devise进行用户认证的Ruby on Rail应用程序。该应用程序的一个要求是,当用户更改密码时,不允许使用与之前使用的最后三个密码相同的密码。为了实现这一点,有一张表包含给定用户的密码历史记录。这些密码是在用户更改密码之前存在的加密密码的副本。加密密码以匹配存储的加密密码。

这是问题出现的地方。我们有一个密码更改表单,用于收集给定用户的新密码。我需要能够获取新密码并对其进行加密,以便我可以将新密码的加密值与历史记录中旧密码的加密值相匹配。

技术性的东西
的Rails 3.0.9版本
设计版本1.3.4
使用标准BCrypt与设计。 bcrypt_ruby版本2.1.4

为此,我们重写了Devise支持的reset_password方法。这允许我们在用户控制器中引入我们自己的方法has_repeated_pa​​ssword。

has_repeated_pa​​ssword我开始用的版本低于:

def has_repeated_password? 
    return false if self.new_record? || self.version == 1 
    histories = self.versions.find(:all, :order => 'version DESC', :limit => 3) 

    histories.detect do |history| 
     history.encrypted_password == self.class.encryptor_class.digest(self.password, self.class.stretches, history.password_salt, self.class.pepper) 
    end 
    end 

的这里的问题是,加密类是从来没有定义,每次导致错误该程序运行。即使有很多例子声称这是有效的,但当Devise使用默认加密时,我无法实现它。

在这方面的一个第二次尝试下面的代码:

def has_repeated_password?<br> 
    return false if self.new_record? || self.version == 1 
    histories = self.versions.find(:all, :order => 'version DESC', :limit => 3) 

    histories.detect do |history| 
     pwd = self.password_digest(self.password) 
     history.encrypted_password == pwd 
    end 
    end 

在这种情况下,我从来没有得到任何匹配存储的密码的密码,即使我已经验证,在数据库中的密码我期望的。

我一直试图挖掘设计代码,看看我能在那里找到。我知道,当它将从用户收集的密码与存储的密码进行匹配时,认证必须以某种方式执行此操作。

任何帮助,将不胜感激。

回答

2

我想我找到了解决我自己的问题。这个关键的问题在于我试图获得一个加密的密码,这个密码不是与Devise绑定的用户模型的一部分。这个解决方案确实假设Devise将使用Bcrypt作为标准加密工具(不记得Devise的哪个版本进行了移动)。 Bcrypt/Devise实际上将加密密码中的salt加密。如果你有盐和胡椒粉,你可以得到相同的密码来生成相同的加密值。

因此,这里是上面refernced例程更新的代码:

def has_repeated_password? 
    return false if self.new_record? || self.version == 1 

    histories = self.versions.find(:all, :order => 'version DESC', :limit => 3) 
    histories.detect do |history| 
     bcrypt = ::BCrypt::Password.new(history.encrypted_password) 
     password = ::BCrypt::Engine.hash_secret("#{self.password}#{self.class.pepper}", bcrypt.salt) 
     password == history.encrypted_password 
    end 
    end 

这里的关键是,Bcyrpt对象具有与利用所产生的原来的密码相同的盐现有加密密码被创建。这是通过给我存储的历史加密密码(history.encrypted_pa​​ssword)来完成的。其他关键要素之一是,历史密码和建议的新密码都使用由Devise管理的相同辣椒。所以通过使用Engne。has_secret调用新的密码,它可以与历史密码进行比较。

我不得不将bcrypt代码移入此处,因为Devise支持的所有密码方法都假定您要对当前用户对象的用户密码进行操作。

+0

使用'p = BCrypt :: Password.new(history.encrypted_pa​​ssword)'然后'p == password'更正确。 'BCrypt :: Password'类有'=='操作符定义。 –