73 lines
1.9 KiB
Go
73 lines
1.9 KiB
Go
package api
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"unicode"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
const (
|
|
minPasswordRunes = 8
|
|
maxPasswordBytes = 72 // bcrypt only uses the first 72 bytes.
|
|
)
|
|
|
|
var commonPasswords = map[string]struct{}{
|
|
"12345678": {},
|
|
"admin123": {},
|
|
"password": {},
|
|
"password123": {},
|
|
"qwerty123": {},
|
|
}
|
|
|
|
func validateNewPassword(username, oldPassword, newPassword string) error {
|
|
if !utf8.ValidString(newPassword) {
|
|
return fmt.Errorf("新密码包含无效字符")
|
|
}
|
|
if newPassword == oldPassword {
|
|
return fmt.Errorf("新密码不能与当前密码相同")
|
|
}
|
|
if utf8.RuneCountInString(newPassword) < minPasswordRunes {
|
|
return fmt.Errorf("新密码至少需要%d个字符", minPasswordRunes)
|
|
}
|
|
if len([]byte(newPassword)) > maxPasswordBytes {
|
|
return fmt.Errorf("新密码不能超过%d字节", maxPasswordBytes)
|
|
}
|
|
if strings.TrimSpace(newPassword) != newPassword {
|
|
return fmt.Errorf("新密码不能以空格开头或结尾")
|
|
}
|
|
for _, char := range newPassword {
|
|
if unicode.IsControl(char) {
|
|
return fmt.Errorf("新密码不能包含控制字符")
|
|
}
|
|
}
|
|
if _, exists := commonPasswords[strings.ToLower(newPassword)]; exists {
|
|
return fmt.Errorf("新密码过于常见,请使用更复杂的密码")
|
|
}
|
|
if username != "" && strings.EqualFold(newPassword, username) {
|
|
return fmt.Errorf("新密码不能与用户名相同")
|
|
}
|
|
|
|
categoryCount := 0
|
|
hasLetter, hasNumber, hasSymbol := false, false, false
|
|
for _, char := range newPassword {
|
|
switch {
|
|
case unicode.IsLetter(char):
|
|
hasLetter = true
|
|
case unicode.IsNumber(char):
|
|
hasNumber = true
|
|
case unicode.IsPunct(char) || unicode.IsSymbol(char):
|
|
hasSymbol = true
|
|
}
|
|
}
|
|
for _, present := range []bool{hasLetter, hasNumber, hasSymbol} {
|
|
if present {
|
|
categoryCount++
|
|
}
|
|
}
|
|
if categoryCount < 2 {
|
|
return fmt.Errorf("新密码至少需要包含字母、数字、符号中的两类")
|
|
}
|
|
return nil
|
|
}
|