【问题标题】:Converting existing password hash to Devise将现有密码哈希转换为设计
【发布时间】:2011-05-24 16:05:06
【问题描述】:

我正在尝试将现有的 Admin 模型转换为 Devise。我们已经有了密码哈希,但显然它与设计不兼容。我想做的是接受登录表单并根据加密密码检查提供的密码。如果不正确,使用旧的哈希检查密码,如果匹配,清空旧的password_hash字段并将Devise的密码设置为提供的密码并保存模型。

前进的最佳方式是什么?我怀疑我需要覆盖某些东西,也许是在自定义控制器中,但我不完全确定如何继续。

【问题讨论】:

    标签: ruby-on-rails devise


    【解决方案1】:

    你可以让Devise用新的crypt方案来做加密密码的“辛苦工作”,如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
    

    【讨论】:

    • 我在测试这个时遇到堆栈级别太深的错误,并在开始后替换 devise_valid_password?(password) 就行了,只是 super -- 似乎工作得更好......
    • 这几乎为我完成了,我只需要更改消化:digest = "#{password}#{password_salt}"20.times { digest = Digest::SHA512.hexdigest(digest) }return false unless digest == encrypted_password
    • 不错的解决方案,尽管我还需要稍作修改——我一直在使用较早版本的 Devise,它创建了加盐 SHA1 哈希。我的支票看起来像这样:digest = "" 10.times { digest = Digest::SHA1.hexdigest("--" &lt;&lt; [password_salt, digest, password, nil].flatten.join("--") &lt;&lt; "--") } return false unless digest == encrypted_password
    • 这是否也适用于带有 Bcrypt 实现的标准 has_secure_password ?
    • 为什么要保留 alias ... 声明?以防万一您有一天想调用旧方法或者它在这里很重要(我不这么认为)?
    【解决方案2】:

    在 Devise 中使用 bcrypt 加密器,这就是我最终对遗留数据所做的事情:

    在models/user.rb中

    # 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 异常,这在对旧用户进行身份验证时会这样做。 我使用它来回退到用于创建原始遗留哈希的哈希算法。

    虽然它不会更改密码,但如果需要,可以简单地将其添加到方法中。

    【讨论】:

    • 某人在您的网站上尝试大量用户名的一个副作用可能是 CPU 使用率非常高,因为他们无意中导致了转换发生。啊,没关系,它不会转换它。所以老用户仍然会不安全......但仍然是一个非常聪明的省力答案,我喜欢它
    【解决方案3】:

    首先您需要将 password_salt 和 encrypted_pa​​ssword 复制到您的新对象模型中

    使用它是因为我必须将我的数据库用户导出到另一个应用程序和旧的, 应用程序正在使用设计 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
    

    【讨论】:

      【解决方案4】:

      如果您从 SHA512 迁移,该解决方案比 moeffju's SHA1 solution 涉及更多:

      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
      

      请务必将 stretchespepper 替换为您用于加密密码的值。

      【讨论】:

        【解决方案5】:

        按照 Thomas Dippel 的说明,我已经做了一个更新密码的要点: 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:
        # http://stackoverflow.com/questions/6113375/converting-existing-password-hash-to-devise
        # 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 
        

        【讨论】:

        • 您可以只使用“self.password = password”,让 Devise 在内部处理所有加密内容。
        • 我认为不可能,因为我想使用 BCrypt 作为加密器,但我的密码是用 authlogic sha512 加密的
        • 有可能;我已经做到了。例如:gist.github.com/1704632 就是您所需要的。验证 SHA,并更新到 Bcrypt。
        • 感谢 moeffju 的回复。我做错了。我正在做额外的工作。 encrypted_password 是在哪里定义的?
        • Devise 在您的用户模型上提供encrypted_password 方法。
        猜你喜欢
        • 2012-06-20
        • 1970-01-01
        • 2011-02-11
        • 2016-11-15
        • 1970-01-01
        • 1970-01-01
        • 2010-11-25
        • 2017-06-23
        • 1970-01-01
        相关资源
        最近更新 更多