83 lines
2.3 KiB
Go
83 lines
2.3 KiB
Go
package validation
|
|
|
|
import (
|
|
"golang.org/x/crypto/bcrypt"
|
|
)
|
|
|
|
// defaultCost 是框架默认 bcrypt cost。
|
|
const defaultCost = 12
|
|
|
|
// normalizeHashCost 将 bcrypt cost 归一化到 bcrypt 支持范围内。
|
|
func normalizeHashCost(cost int) int {
|
|
if cost < bcrypt.MinCost {
|
|
return bcrypt.MinCost
|
|
}
|
|
if cost > bcrypt.MaxCost {
|
|
return bcrypt.MaxCost
|
|
}
|
|
return cost
|
|
}
|
|
|
|
// normalizeUpgradeTargetCost 归一化升级目标 cost,异常值回退到框架默认值。
|
|
func normalizeUpgradeTargetCost(cost int) int {
|
|
if cost <= 0 || cost > bcrypt.MaxCost {
|
|
return defaultCost
|
|
}
|
|
return normalizeHashCost(cost)
|
|
}
|
|
|
|
// HashPassword 使用框架默认 cost 哈希明文密码。
|
|
func HashPassword(password string) (string, error) {
|
|
return HashPasswordWithCost(password, defaultCost)
|
|
}
|
|
|
|
// HashPasswordWithCost 使用指定 cost 哈希明文密码。
|
|
//
|
|
// 低于 bcrypt.MinCost 的 cost 会提升到 bcrypt.MinCost,高于 bcrypt.MaxCost
|
|
// 的 cost 会降到 bcrypt.MaxCost。
|
|
func HashPasswordWithCost(password string, cost int) (string, error) {
|
|
cost = normalizeHashCost(cost)
|
|
|
|
bytes, err := bcrypt.GenerateFromPassword([]byte(password), cost)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(bytes), nil
|
|
}
|
|
|
|
// CheckPassword 校验 hashedPassword 是否匹配明文密码。
|
|
func CheckPassword(hashedPassword, password string) bool {
|
|
err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
|
|
return err == nil
|
|
}
|
|
|
|
// CheckPasswordAndUpgrade 校验密码,并在存量 hash 的 cost 低于归一化后的目标 cost 时重新哈希。
|
|
func CheckPasswordAndUpgrade(hashedPassword, password string, targetCost int) (match bool, needUpgrade bool, newHash string, err error) {
|
|
targetCost = normalizeUpgradeTargetCost(targetCost)
|
|
|
|
err = bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
|
|
if err != nil {
|
|
return false, false, "", err
|
|
}
|
|
|
|
cost, err := bcrypt.Cost([]byte(hashedPassword))
|
|
if err != nil {
|
|
return true, false, "", nil
|
|
}
|
|
|
|
if cost < targetCost {
|
|
newHash, err = HashPasswordWithCost(password, targetCost)
|
|
if err != nil {
|
|
return true, false, "", err
|
|
}
|
|
return true, true, newHash, nil
|
|
}
|
|
|
|
return true, false, "", nil
|
|
}
|
|
|
|
// GetPasswordCost 返回 hashedPassword 中编码的 bcrypt cost。
|
|
func GetPasswordCost(hashedPassword string) (int, error) {
|
|
return bcrypt.Cost([]byte(hashedPassword))
|
|
}
|