Swift 如何处理在Vapor 3中添加密码哈希?

Swift 如何处理在Vapor 3中添加密码哈希?,swift,vapor,Swift,Vapor,我正在构建一个基本的身份验证设置,类似于Vapor的auth模板中使用的身份验证设置。我已将所有内容设置为与模板中相同的方式 不过,我想加盐。我可以在创建时为用户生成salt: static func create(_ req: Request, newUserRequest user: CreateUserRequest) throws -> Future<User.Public> { return User.query(on: req).filter(\.usern

我正在构建一个基本的身份验证设置,类似于Vapor的
auth模板中使用的身份验证设置。我已将所有内容设置为与模板中相同的方式

不过,我想加盐。我可以在创建时为用户生成salt:

static func create(_ req: Request, newUserRequest user: CreateUserRequest) throws -> Future<User.Public> {
    return User.query(on: req).filter(\.username == user.username).first().flatMap { existingUser in
        guard existingUser == nil else {
            throw Abort(.badRequest, reason: "A user with the given username already exists.")
        }

        guard user.password == user.passwordVerification else {
            throw Abort(.badRequest, reason: "Given passwords did not match.")
        }

        let count = 16
        var pw_salt_data = Data(count: count)
        let _ = pw_salt_data.withUnsafeMutableBytes { mutableBytes in
            SecRandomCopyBytes(kSecRandomDefault, count, mutableBytes)
        }
        let pw_salt = try BCrypt.hash(pw_salt_data.base64EncodedString())

        let pw_hash = try BCrypt.hash(pw_salt + user.password)

        return User(id: nil, username: user.username, pw_hash: pw_hash, pw_salt: pw_salt, email: user.email).save(on: req).toPublic()
    }
}
static func create(req:Request,newUserRequest user:CreateUserRequest)抛出->未来{
返回User.query(on:req).filter(\.username==User.username).first().flatMap{existingUser in
guard existingUser==nil else{
抛出中止(.badRequest,原因:“具有给定用户名的用户已存在。”)
}
guard user.password==user.passwordVerification else{
抛出中止(.badRequest,原因:“给定的密码不匹配。”)
}
让计数=16
var pw_盐_数据=数据(计数:计数)
让u=pw_salt_data.withunsafemtablebytes{mutableBytes in
SecRandomCopyBytes(kSecRandomDefault、count、mutableBytes)
}
让pw_salt=try BCrypt.hash(pw_salt_data.base64EncodedString())
让pw_hash=try BCrypt.hash(pw_salt+user.password)
返回用户(id:nil,username:User.username,pw_散列:pw_散列,pw_salt:pw_salt,email:User.email)。保存(on:req).toPublic()
}
}
但在登录期间执行身份验证时,无法检索该salt:

static func login(_ req: Request) throws -> Future<UserToken> {
    let user = try req.requireAuthenticated(User.self)
    let token = try UserToken.create(userID: user.requireID())
    return token.save(on: req)
}
static func登录(\req:Request)抛出->未来{
let user=try请求重新验证(user.self)
让token=try UserToken.create(userID:user.requireID())
返回令牌.save(on:req)
}
我希望为每个用户随机生成salt,并将其作为散列密码的一个单独列存储在数据库中,以便稍后在身份验证期间使用


Vapor 3中是否有一种标准化的方法来处理salt密码散列?

Vapor中的工作方式是,每个BCrypt散列都有一个唯一的salt,该salt与数据库中的密码一起保存。除此之外,Vapor中的BCrypt默认函数


如果你想走另一条路,可以看看散列密码的函数——这需要一点时间。然后,您可以将其保存在自己的字段中,并在验证密码时检索和。老实说,我建议只使用默认值,除非您有非常具体的理由不使用BCrypt散列密码。BCrypt已经是蒸汽依赖性的一部分

BCrypt.hash("vapor", cost: 4)
这将使用随机生成的salt对字符串“vapor”进行散列,复杂度为4。选择成本是主观和随意的,但建议实际安全应用程序的成本系数应高于10-12。如果您不喜欢BCrypt随机生成的salt,并且希望生成自己的salt,则可以将salt提供给哈希函数,该函数具有以下签名:

public func hash(_ plaintext: LosslessDataConvertible, cost: Int = 12, salt: LosslessDataConvertible? = nil) throws -> String 
文档说明,如果手动提供,salt必须为16字节。 这是一个示例哈希:

$2a$04$/nqhWqplnughhq6mlKmi8.raprxoG/dczY8kdbOKm.zC5sPu.2IBi
如您所见,它包括辅助信息,如复杂性、算法类型和salt,以及进行验证所需的一切。如果你自己提供盐,它也将是最终散列的一部分,你不需要单独提供。您可以按照下面的说明进行验证

try BCrypt.verify("vapor", created: hashedPasswordSavedInDatabase)

哦,我明白了,我没有意识到BCrypt会自动生成一个salt并将其存储在相同的数据中!谢谢你的解释。我想我会坚持默认实现,因为我对它有了更好的理解。干杯我懂了!从您的评论中,我可以找到更多的文档和文档。我没有意识到BCrypt会自动为我生成盐。现在我更好地理解了这一点,我将坚持默认实现。非常感谢。