web-dev-qa-db-ja.com

工夫:パスワードを手動で暗号化して直接保存する

古いデータベースから大量のユーザーを移行しようとしています。これを行うには、activerecord-importを使用し、すべてのユーザーデータをDBに直接保存しようとしています(ユーザーモデルをバイパスします)。

私の問題:古いユーザーのプレーンテキストパスワードを取得して暗号化し、DBに直接保存する必要があります。 generate Deviseを使用してパスワードを作成する方法は知っていますが、データベースに直接保存できるハッシュされたパスワードを取得する方法があるかどうか疑問に思っています。

やりたいこと:

new_hashed_password = Devise.awesome_encrypting_method(old_user.password)

次に、モデルを経由せずに「new_hashed_pa​​ssword」を直接DBに保存します。私はDeviseを掘り下げて、これを見つけました:

def password_digest(password)
  ::BCrypt::Password.create("#{password}#{self.class.pepper}", :cost => self.class.stretches).to_s
end

@@ stretchesのデフォルトは10(lib/devise.rb:71)であり、イニシャライザーによってオーバーライドされません

@@ pepperのデフォルトはnil(lib/devise.rb:148)であり、イニシャライザーによってオーバーライドされません

Password_digest()を手動で再作成できると思ったが、パスワードとストレッチを設定しても、結果のハッシュは毎回異なるため、Bcryptの基本的な何かが欠けていると思う。

何か案は?ご協力いただきありがとうございます!

32
jmccartie

良いニュースと悪いニュース。

良いニュース:

以下は、ユーザーのパスワードを手動で作成するために機能します。

 pepper = nil
 cost = 10
 encrypted_password = ::BCrypt::Password.create("#{password}#{pepper}", :cost => cost).to_s

コショウとコ​​ストは、devise initializerで見つけることができます。この方法は、Deviseの「valid_password?」を使用して確認されました方法。

悪いニュース:

「User.new(password:password).encrypted_pa​​ssword」を避けようとしたすべての理由は、速度のためでした。とても遅いです。インポートタスクの他のすべての部分で、これを意図的に回避しました。

しかし、結局のところ、ここでの主なコストはUserオブジェクトのインスタンス化ではなく、BCrypt自体です。意図的に低速に設計されているため、BCryptを直接使用する場合、顕著な速度向上はほとんどありません。

私の最後の答え:それを吸い上げ、レーキスクリプトを実行し、飲み物を探しに行きます。

19
jmccartie

次のようにする必要があります。

password = 'the secret password'
new_hashed_password = User.new(:password => password).encrypted_password

これは、コードからパスワードが生成される方法を抽象化してBCryptを直接使用するよりもはるかに優れており、理解しやすく、またdeviseが暗号化パスワードを作成する方法の変更に影響されません。あなたのコードはそうすべきではなく、それについて何かを知る理由はありません。

58
Robert Kajic

上記の他の答えはどれも私には役に立たなかったので、ここに私がやったことです:

user.valid_password?(plain_password)

https://github.com/plataformatec/devise/blob/d293e00ef5f431129108c1cbebe942b32e6ba616/lib/devise/models/database_authenticatable.rb#L44

3
user645579

別の方法は次のとおりです。User.new.send(:password_digest, 'xxx')

2
Paul Odeon

「users」テーブルと「password」列を持つmysqlデータベースと、考案するために接続される「user」というActiveRecordモデルクラスがあると仮定します。

アプリでActiveRecordモデルクラスを作成しますapp/models/old_user.rb

OldUser < ActiveRecord::Base
  set_table :users
  establish_connection :database => "old_database", :user => "old user", :adapter => "mysql"
end

次に、rakeタスクを作成します:app/lib/tasks/migrate_users.rake

task :migrate_users => :environment do
  OldUser.find_each do |old_user|
    u = User.new(:email => old_user.email, :password => old_user.password, :password_confirmation => old_user.password);
    #if your using confirmation
    u.skip_confirmation!
    u.save!
  end
end

必要に応じて変更します(アプリ固有のユーザー属性を保存していることを確認してください)

Then$ rake migrate_users

1
jacobsimeon