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 }