我对玩框架和密码哈希有点陌生。我试图找到一些散列密码的解决方案,然后我找到了 BCrypt。您认为这足以散列密码吗?如果它很好,我怎样才能让它在播放框架中工作?(我正在使用 play 2.1.3)谢谢!
4 回答
这是我编写的一个示例 Play Java 项目,它使用 BCrypt 对密码进行哈希处理,请参阅 newUser() 和 signIn() 操作:
https://github.com/jroper/play-demo-twitbookplus/blob/master/app/controllers/UserController.java
你可以在 Scala 中做类似的事情。总结一下,将 jbycrpt 添加到 Build.scala 中的依赖项中:
val appDependencies = Seq(
"org.mindrot" % "jbcrypt" % "0.3m"
)
然后使用此哈希密码:
String passwordHash = BCrypt.hashpw(password, BCrypt.gensalt());
并使用以下方法验证密码:
BCrypt.checkpw(password, passwordHash)
更新 (2020)
这些天在我的项目中,我不再使用 BCrypt,而是使用 PBKDF2 哈希,它的优点是不需要任何额外的依赖项,但缺点是需要编写更多代码并手动管理 salt。BCrypt 也存在一些问题,不同的实现无法准确地消耗彼此的输出,有些实现甚至截断长密码,这真的很糟糕。尽管它的代码要多得多,但我喜欢这种方法,因为它给了我更多的控制权,并且它透明地准确地显示了事情是如何工作的,并且随着散列算法和输入参数的建议不断变化,随着时间的推移更新事情变得容易。
无论如何,这是我使用的代码,它将盐和使用的迭代次数(以便这些可以按照最佳实践建议随着时间的推移而增加)存储在散列值中,用冒号分隔:
val DefaultIterations = 10000
val random = new SecureRandom()
private def pbkdf2(password: String, salt: Array[Byte], iterations: Int): Array[Byte] = {
val keySpec = new PBEKeySpec(password.toCharArray, salt, iterations, 256)
val keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256")
keyFactory.generateSecret(keySpec).getEncoded
}
def hashPassword(password: String, salt: Array[Byte]): String = {
val salt = new Array[Byte](16)
random.nextBytes(salt)
val hash = pbkdf2(password, salt, DefaultIterations)
val salt64 = Base64.getEncoder.encodeToString(salt)
val hash64 = Base64.getEncoder.encodeToString(hash)
s"$DefaultIterations:$hash64:$salt64"
}
def checkPassword(password: String, passwordHash: String): Boolean = {
passwordHash.split(":") match {
case Array(it, hash64, salt64) if it.forall(_.isDigit) =>
val hash = Base64.getDecoder.decode(hash64)
val salt = Base64.getDecoder.decode(salt64)
val calculatedHash = pbkdf2(password, salt, it.toInt)
calculatedHash.sameElements(hash)
case other => sys.error("Bad password hash")
}
}
我的实际代码有点复杂,我包含了一个版本化的魔法词作为第一个组件 ( ph1:
),这意味着如果我决定更改未在输出值中编码的散列算法或其他输入参数,我可以通过通过将魔术词更新为 对这些哈希进行编码ph2:
,然后我可以拥有验证旧哈希ph1
和新ph2
哈希的代码。
BCrypt 非常适合散列密码。使用Silhouette并为您的密码插件使用BCrypt 密码哈希。
这个项目为 jbcrypt https://github.com/t3hnar/scala-bcrypt提供了一个很好的 scala 包装器- 过去使用过它并且效果很好。
以下是最新的 bcrypt t3hnar 库版本 4.1 与 Play 框架一起使用的说明:
将依赖项添加到 build.sbt:
libraryDependencies += "com.github.t3hnar" %% "scala-bcrypt" % "4.1"
将 Hash 对象添加到您的项目中:
// Reference: https://github.com/t3hnar/scala-bcrypt
package utilities
import com.github.t3hnar.bcrypt._
import play.api.Logging
import scala.util.Success
import scala.util.Failure
object Hash extends Logging {
private val log = Log.get
def create(value: String): String = {
log.debug("Encrypting a value")
// Creating a salted hash
val salt = generateSalt
val hash = value.bcrypt(salt)
// return hashed value
hash
}
def validate(value: String, hash: String): Boolean = {
// Validating the hash
value.isBcryptedSafe(hash) match {
case Success(result) => { // hash is valid - correct salt and number of rounds
log.trace("Hash is safe")
if (result) log.trace("Test hash matches stored hash") else log.trace("Test hash does not match stored hash")
result // true if test hash matches the stored has, false if it does not
}
case Failure(failure) => {
// Hash is invalid
log.trace("Hash is not safe")
false
}
}
}
}
使用示例:
// Password hashing
val hash = Hash.create(password)
// Password validation
def authenticate(email: String, password: String): Option[User] = {
log.debug(s"Authenticating user: $email")
// Get user
read(email) match {
case Some(user) => {
// Compare password with the hashed value
if (Hash.validate(password, user.hash)) Some(user) else None
}
case None => {
// Cannot find user with this email
log.trace(s"User not found")
None
}
}
}
哈希实用程序示例:https ://github.com/LineDrop/play-scala-web-application/blob/master/app/utilities/Hash.scala
用户模型示例:https ://github.com/LineDrop/play-scala-web-application/blob/master/app/models/User.scala