Meteor 使用 bcrypt 从不同的服务器验证 email/password
Meteor Authenticate email/password from a different server with bcrypt
我想让我的 meteor
用户通过 ruby
应用程序登录。
我在哪里
- 我有两个网站,都在同一个域中,共享相同的 MongoDB。
- 一个是
accounts-password
的 METEOR 应用程序(使用 bcrypt)
- 另一个是 RUBY ON RAILS-app,它使用
devise
(也使用 bcrypt)进行身份验证。
- 我可以在这两个应用程序上分别注册和登录。
当我将 encrypted_password 从 Meteor 的 "bcrypt" 字段传输 (copy/paste) 到 Ruby 的 "encrypted_password" 并尝试登录时,我被拒绝了。反之亦然。然后我在我的 ruby 应用程序中重新创建了 meteor 应用程序的那种加盐(SHA-256 plain-password-hashing before they got compared against)。
(这里是流星账号-密码源文件(https://github.com/meteor/meteor/blob/oplog-backlog-on-1.0.3.1/packages/accounts-password/password_server.js))
这是我的 Ruby 实现:
class BCryptSHA256Hasher < Hasher
def initialize
@algorithm = :bcrypt_sha256
@cost = 10
@digest = OpenSSL::Digest::SHA256.new
end
def salt
BCrypt::Engine.generate_salt(@cost)
end
def get_password_string(password)
@digest.digest(password) unless @digest.nil?
end
def encode(password, salt)
password = get_password_string(password)
hash = BCrypt::Engine.hash_secret(password, salt)
return hash
end
def verify(password, encoded)
password_digest = get_password_string(password)
hash = BCrypt::Engine.hash_secret(password_digest, encoded)
# password = "asdfasdf"
# encoded = "a$FqvtI7zNgmdWJJG1n9JwZewVYrzEn38JIxEGwmMviMsZsrCmYHqWm"
# hash = "a$FqvtI7zNgmdWJJG1n9JwZe22XU1hRDSNtHIrnYve9FbmjjqJCLhZi"
# constant_time_comparison:
constant_time_compare(encoded, hash)
end
def constant_time_compare(a, b)
check = a.bytesize ^ b.bytesize
a.bytes.zip(b.bytes) { |x, y| check |= x ^ y }
check == 0
end
end
这是一个有效的 User
文档,两个服务器都将使用它:
{
"_id": "g4BPfpavJGGTNgJcE",
"authentication_token": "iZqmCsYS1Y9Xxh6t22-X",
"confirmed_at": new Date(1457963598783),
"createdAt": new Date(1457963456581),
"current_sign_in_at": new Date(1457966356123),
"current_sign_in_ip": "127.0.0.1",
"email": "demo@demo.com",
"emails": [
{
"address": "demo@demo.com",
"verified": true
}
],
"encrypted_password": "a/PJw51HgXfzYJWpaBHGj.QoRCTl0E29X0ZYTZPQhLRo69DGi8Xou",
"failed_attempts": 0,
"last_sign_in_at": new Date(1457966356123),
"last_sign_in_ip": "127.0.0.1",
"profile": {
"_id": ObjectId("56e6c1e7a54d7595e099da27"),
"firstName": "asdf",
"lastName": "asdf"
},
"reset_password_sent_at": null,
"reset_password_token": null,
"services": {
"_id": ObjectId("56e6c1e7a54d7595e099da28"),
"password": {
"bcrypt": "a/PJw51HgXfzYJWpaBHGj.QoRCTl0E29X0ZYTZPQhLRo69DGi8Xou"
},
"resume": {
"loginTokens": [
]
}
},
"sign_in_count": 1,
"updated_at": new Date(1457966356127),
"username": "mediatainment"
}
我认为@maxpleaner 的评论是处理身份验证的最佳方式。但如果真的需要单独验证用户,那就只需要猴子补丁设计。
config/initializers/devise_meteor_adapter.rb
module DeviseMeteorAdapter
def digest(klass, password)
klass.pepper = nil
password = ::Digest::SHA256.hexdigest(password)
super
end
def compare(klass, hashed_password, password)
klass.pepper = nil
password = ::Digest::SHA256.hexdigest(password)
super
end
end
Devise::Encryptor.singleton_class.prepend(DeviseMeteorAdapter)
警告:未经测试。
我想让我的 meteor
用户通过 ruby
应用程序登录。
我在哪里
- 我有两个网站,都在同一个域中,共享相同的 MongoDB。
- 一个是
accounts-password
的 METEOR 应用程序(使用 bcrypt) - 另一个是 RUBY ON RAILS-app,它使用
devise
(也使用 bcrypt)进行身份验证。 - 我可以在这两个应用程序上分别注册和登录。
当我将 encrypted_password 从 Meteor 的 "bcrypt" 字段传输 (copy/paste) 到 Ruby 的 "encrypted_password" 并尝试登录时,我被拒绝了。反之亦然。然后我在我的 ruby 应用程序中重新创建了 meteor 应用程序的那种加盐(SHA-256 plain-password-hashing before they got compared against)。
(这里是流星账号-密码源文件(https://github.com/meteor/meteor/blob/oplog-backlog-on-1.0.3.1/packages/accounts-password/password_server.js))
这是我的 Ruby 实现:
class BCryptSHA256Hasher < Hasher
def initialize
@algorithm = :bcrypt_sha256
@cost = 10
@digest = OpenSSL::Digest::SHA256.new
end
def salt
BCrypt::Engine.generate_salt(@cost)
end
def get_password_string(password)
@digest.digest(password) unless @digest.nil?
end
def encode(password, salt)
password = get_password_string(password)
hash = BCrypt::Engine.hash_secret(password, salt)
return hash
end
def verify(password, encoded)
password_digest = get_password_string(password)
hash = BCrypt::Engine.hash_secret(password_digest, encoded)
# password = "asdfasdf"
# encoded = "a$FqvtI7zNgmdWJJG1n9JwZewVYrzEn38JIxEGwmMviMsZsrCmYHqWm"
# hash = "a$FqvtI7zNgmdWJJG1n9JwZe22XU1hRDSNtHIrnYve9FbmjjqJCLhZi"
# constant_time_comparison:
constant_time_compare(encoded, hash)
end
def constant_time_compare(a, b)
check = a.bytesize ^ b.bytesize
a.bytes.zip(b.bytes) { |x, y| check |= x ^ y }
check == 0
end
end
这是一个有效的 User
文档,两个服务器都将使用它:
{
"_id": "g4BPfpavJGGTNgJcE",
"authentication_token": "iZqmCsYS1Y9Xxh6t22-X",
"confirmed_at": new Date(1457963598783),
"createdAt": new Date(1457963456581),
"current_sign_in_at": new Date(1457966356123),
"current_sign_in_ip": "127.0.0.1",
"email": "demo@demo.com",
"emails": [
{
"address": "demo@demo.com",
"verified": true
}
],
"encrypted_password": "a/PJw51HgXfzYJWpaBHGj.QoRCTl0E29X0ZYTZPQhLRo69DGi8Xou",
"failed_attempts": 0,
"last_sign_in_at": new Date(1457966356123),
"last_sign_in_ip": "127.0.0.1",
"profile": {
"_id": ObjectId("56e6c1e7a54d7595e099da27"),
"firstName": "asdf",
"lastName": "asdf"
},
"reset_password_sent_at": null,
"reset_password_token": null,
"services": {
"_id": ObjectId("56e6c1e7a54d7595e099da28"),
"password": {
"bcrypt": "a/PJw51HgXfzYJWpaBHGj.QoRCTl0E29X0ZYTZPQhLRo69DGi8Xou"
},
"resume": {
"loginTokens": [
]
}
},
"sign_in_count": 1,
"updated_at": new Date(1457966356127),
"username": "mediatainment"
}
我认为@maxpleaner 的评论是处理身份验证的最佳方式。但如果真的需要单独验证用户,那就只需要猴子补丁设计。
config/initializers/devise_meteor_adapter.rb
module DeviseMeteorAdapter
def digest(klass, password)
klass.pepper = nil
password = ::Digest::SHA256.hexdigest(password)
super
end
def compare(klass, hashed_password, password)
klass.pepper = nil
password = ::Digest::SHA256.hexdigest(password)
super
end
end
Devise::Encryptor.singleton_class.prepend(DeviseMeteorAdapter)
警告:未经测试。