|
@@ -15,6 +15,7 @@ import (
|
|
|
"strconv"
|
|
|
"strings"
|
|
|
"sync"
|
|
|
+ "unicode/utf8"
|
|
|
)
|
|
|
|
|
|
// validate 是一个单例的 validator 实例,确保全局只有一个验证器,以提高性能。
|
|
@@ -156,10 +157,6 @@ func validateHostPort(fl validator.FieldLevel) bool {
|
|
|
return false // 格式不正确,或者主机或端口为空
|
|
|
}
|
|
|
|
|
|
- // 检查主机部分是否为 IP 地址
|
|
|
- if net.ParseIP(host) != nil {
|
|
|
- return true
|
|
|
- }
|
|
|
|
|
|
portInt, err := strconv.Atoi(port)
|
|
|
if err != nil {
|
|
@@ -169,6 +166,13 @@ func validateHostPort(fl validator.FieldLevel) bool {
|
|
|
return false
|
|
|
}
|
|
|
|
|
|
+ // 检查主机部分是否为 IP 地址
|
|
|
+ if net.ParseIP(host) != nil {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
// --- 如果不是 IP,则按域名处理(这是支持中文域名的关键部分) ---
|
|
|
|
|
|
// 4. 将可能为中文的域名转换为 Punycode (ASCII) 格式
|
|
@@ -190,18 +194,37 @@ func isIdnFqdn(fl validator.FieldLevel) bool {
|
|
|
// 获取字段的值
|
|
|
domain := fl.Field().String()
|
|
|
|
|
|
- // omitempty 的行为由 validator 自身处理,
|
|
|
- // 如果字段为空,我们的函数不应该判定为 false。
|
|
|
+ // omitempty 的行为由 validator 自身处理
|
|
|
if domain == "" {
|
|
|
return true
|
|
|
}
|
|
|
|
|
|
- // idna.ToASCII 会将域名转换为 Punycode 格式。
|
|
|
- // 这个过程本身就包含了对 IDNA2008 标准的严格验证。
|
|
|
- // 如果域名格式不正确(例如包含非法字符、标签过长等),它会返回一个错误。
|
|
|
- // 因此,我们只需要检查这个函数是否返回错误即可。
|
|
|
+ // 关键步骤 1:一个有效的 FQDN 必须包含至少一个点来分隔 TLD。
|
|
|
+ // 这会直接拦截掉 "你好世界" 这样的输入。
|
|
|
+ if !strings.Contains(domain, ".") {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ // 关键步骤 2:FQDN 不能以点开头或结尾。
|
|
|
+ // 注意:idna.ToASCII 会检查标签是否以连字符开头/结尾,所以我们只需要检查点。
|
|
|
+ if strings.HasPrefix(domain, ".") || strings.HasSuffix(domain, ".") {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ // 关键步骤 3:检查是否为有效的 UTF-8 字符串。
|
|
|
+ // 这是防止非 UTF-8 编码字节流的关键。
|
|
|
+ if !utf8.ValidString(domain) {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ // 关键步骤 4:使用 idna.ToASCII 进行最严格的格式验证。
|
|
|
+ // 它会检查:
|
|
|
+ // - 是否包含非法字符(如 @, :, / 等)。
|
|
|
+ // - 每个标签(点之间的部分)是否过长。
|
|
|
+ // - 每个标签是否以连字符(-)开头或结尾。
|
|
|
+ // - 以及其他所有 IDNA2008 标准中定义的规则。
|
|
|
_, err := idna.ToASCII(domain)
|
|
|
|
|
|
- // 如果没有错误,说明它是一个有效的(可能是国际化的)域名。
|
|
|
+ // 如果没有错误,说明它在格式上是一个有效的 FQDN。
|
|
|
return err == nil
|
|
|
}
|