Ruby-on-rails 设计:手动加密密码并直接存储

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8015758/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 02:17:52  来源:igfitidea点击:

Devise: manually encrypt password and store directly

ruby-on-railsdevise

提问by jmccartie

I'm trying to migrate a ton of users from an old database. To do this, I'm using activerecord-import and trying to save all my user data directly to DB (bypassing the User model).

我正在尝试从旧数据库迁移大量用户。为此,我使用 activerecord-import 并尝试将所有用户数据直接保存到数据库(绕过用户模型)。

My issue: I need to take the old user's plain-text password, encrypt it, and store directly to the DB. I know how to generatea password using Devise, but am wondering if there's a way to get a hashed password that I can store directly to the database.

我的问题:我需要获取旧用户的纯文本密码,对其进行加密,然后直接存储到数据库中。我知道如何使用 Devise生成密码,但我想知道是否有办法获取可以直接存储到数据库的散列密码。

Hoping to do:

希望做到:

new_hashed_password = Devise.awesome_encrypting_method(old_user.password)

Then store "new_hashed_password" directly into the DB without going through the model. I dug around in Devise and found this:

然后将“new_hashed_pa​​ssword”直接存储到数据库中,无需经过模型。我在设计中挖掘并发现了这个:

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

@@stretches defaults to 10 (lib/devise.rb:71) and isn't overridden by my initializer

@@stretches 默认为 10 (lib/devise.rb:71) 并且不会被我的初始化程序覆盖

@@pepper defaults to nil (lib/devise.rb:148) and isn't overridden by my initializer

@@pepper 默认为 nil (lib/devise.rb:148) 并且不会被我的初始化程序覆盖

I thought I could manually re-create password_digest() but I think I'm missing something fundamental about Bcrypt because even with setting password and stretches, the resulting hash is different every time.

我以为我可以手动重新创建 password_digest() 但我认为我缺少有关 Bcrypt 的一些基本知识,因为即使设置了密码和拉伸,生成的哈希值每次都不同。

Any ideas? Thanks for your help!

有任何想法吗?谢谢你的帮助!

采纳答案by jmccartie

Good news and bad news.

好消息和坏消息。

Good news:

好消息:

The following works to create your user's password manually.

以下工作用于手动创建您的用户密码。

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

You can find your pepper and cost in your devise initializer. This method was confirmed using Devise's "valid_password?" method.

你可以在你的设计初始化器中找到你的胡椒和成本。使用 Devise 的“valid_password?”确认了这种方法。方法。

Bad news:

坏消息:

The entire reason I was trying to avoid "User.new(password: password).encrypted_password" was because of speed. It's terribly slow. With all my other pieces of my import task, I've intentionally avoided this.

我试图避免“User.new(password: password).encrypted_pa​​ssword”的全部原因是因为速度。它非常慢。对于我的所有其他导入任务,我有意避免了这一点。

But as it turns out, the major cost here is not instantiating a User object -- but BCrypt itself. There is very little noticeable speed boost when using BCrypt directly because it's intentionally designed to be slow.

但事实证明,这里的主要成本不是实例化 User 对象——而是 BCrypt 本身。直接使用 BCrypt 时几乎没有明显的速度提升,因为它被故意设计为很慢。

My final answer: suck it up, run the rake script, go find a beverage.

我的最终答案是:吸吮它,运行 rake 脚本,去寻找饮料。

回答by Robert Kajic

You should do it like this:

你应该这样做:

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

This is much better than using BCrypt directly as it abstracts away how passwords are generated from your code, making it easier to understand, and also immune to changes in how devise constructs encrypted passwords. Your code should not, and has no reason to know anything about that.

这比直接使用 BCrypt 好得多,因为它抽象了如何从代码中生成密码,使其更容易理解,并且不受设计构造加密密码方式的变化的影响。你的代码不应该,也没有理由知道任何关于它的信息。

回答by jmccartie

None of the other answers above worked for me, so here is what I did:

上面的其他答案都不适合我,所以这就是我所做的:

user.valid_password?(plain_password)

user.valid_password?(plain_password)

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

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

回答by Paul Odeon

An alternative method is: User.new.send(:password_digest, 'xxx')

另一种方法是: User.new.send(:password_digest, 'xxx')

回答by jacobsimeon

Assuming you have a mysql database with a "users" table and a "password" column And an ActiveRecord model class called "user" that is hooked up to devise

假设您有一个 mysql 数据库,其中包含一个“users”表和一个“password”列以及一个名为“user”的 ActiveRecord 模型类,该类连接到设计

Create an ActiveRecord model class in your app app/models/old_user.rb

在您的应用app/models/old_user.rb 中创建一个 ActiveRecord 模型类

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

then create a rake task: app/lib/tasks/migrate_users.rake

然后创建一个 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

Modify as necessary (make sure you're saving any app-specific user attributes)

根据需要进行修改(确保您保存了任何特定于应用程序的用户属性)

Then$ rake migrate_users

然后$ rake migrate_users