如何用Go语言规则引擎实现Golang密码强度检测器的初级实战?

2026-04-30 13:152阅读0评论SEO问题
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1034个文字,预计阅读时间需要5分钟。

如何用Go语言规则引擎实现Golang密码强度检测器的初级实战?

Go 的 `regexp.MustCompile` 默认是贪婪匹配,不支持必须同时满足多个条件的原生断言(例如至少一个数字 AND 至少一个大写字母)。

  • ^.*$ 包裹整个正则没用,它只是匹配整行,不解决逻辑组合问题
  • regexp.FindAllString 不能告诉你“缺哪一类”,只返回匹配到的子串
  • 多个 MatchString 调用是可行解法,但要注意:空字符串或全空白符时,strings.TrimSpace 必须前置,否则 " " 会被误判为“含字母”

示例片段:

func checkPassword(s string) bool { s = strings.TrimSpace(s) if len(s) < 8 { return false } hasUpper := regexp.MustCompile(`[A-Z]`).MatchString(s) hasLower := regexp.MustCompile(`[a-z]`).MatchString(s) hasDigit := regexp.MustCompile(`[0-9]`).MatchString(s) hasSpecial := regexp.MustCompile(`[^a-zA-Z0-9\s]`).MatchString(s) return hasUpper && hasLower && hasDigit && hasSpecial }

unicode 包比正则更稳的场景

当你要区分“中文字符”“Emoji”“全角标点”或“控制字符”时,regexp 很难写对(比如 \p{Han} 在 Go 的 regexp 中不支持)。此时直接遍历 rune,用 unicode.IsLetterunicode.IsDigitunicode.IsPunct 等判断更可靠,也更容易调试。

  • unicode.IsLetter(r) 覆盖中日韩英等所有字母,不含数字和下划线
  • unicode.IsPunct(r) 匹配标点,但不包括空格、制表符、换行符(那些归 unicode.IsSpace 管)
  • 遇到 Emoji(如 ?)时,unicode.IsSymbol(r) 才能捕获,IsPunct 会返回 false
  • 不要对每个 rune 都调用 unicode.Is* 四次——先缓存结果,或用位掩码一次收集多类特征

规则引擎不是必须写 DSL:从 map[string]func(string) bool 开始

初学 Go 规则引擎,别一上来就搞 AST 解析或 YACC。密码强度本质是一组独立校验函数的合取(AND),用 map 存规则名和函数,再统一执行即可:

立即学习“go语言免费学习笔记(深入)”;

type Rule struct { Name string Check func(string) bool Msg string } <p>var passwordRules = []Rule{ {"minLength", func(s string) bool { return len(strings.TrimSpace(s)) >= 8 }, "too short"}, {"hasUpper", func(s string) bool { return regexp.MustCompile(<code>[A-Z]</code>).MatchString(s) }, "no uppercase"}, {"hasLower", func(s string) bool { return regexp.MustCompile(<code>[a-z]</code>).MatchString(s) }, "no lowercase"}, {"hasDigit", func(s string) bool { return regexp.MustCompile(<code>[0-9]</code>).MatchString(s) }, "no digit"}, }

  • 规则顺序无关,但错误提示顺序建议按用户感知重要性排(长度 → 字符类型)
  • Check 函数内部别做耗时操作(如 HTTP 请求、文件读取),规则引擎必须低延迟
  • 如果后续要支持“警告级规则”(如“连续重复字符 >3”),可加 Level int 字段区分 error/warn

测试时最容易忽略的边界:空格、BOM、零宽字符

真实用户粘贴密码时,可能带不可见字符:\uFEFF(BOM)、\u200B(零宽空格)、\u00A0(不间断空格)。这些字符会让 len(s)len(strings.TrimSpace(s)) 不一致,导致“明明输够 8 位却提示太短”。

  • 测试用例必须显式包含:"abc123AB\u200B""\uFEFFpassword123""pass word123"(中间有全角空格)
  • 建议在入口统一清理:strings.Map(func(r rune) rune { if unicode.IsControl(r) || unicode.IsMark(r) { return -1 }; return r }, s)
  • unicode.IsMark(r) 能干掉组合字符(如变音符号),避免 “café” 被误判为含非法字符

规则越贴近真实输入,越容易暴露清理逻辑的缺口。别只测干净 ASCII 字符串。

本文共计1034个文字,预计阅读时间需要5分钟。

如何用Go语言规则引擎实现Golang密码强度检测器的初级实战?

Go 的 `regexp.MustCompile` 默认是贪婪匹配,不支持必须同时满足多个条件的原生断言(例如至少一个数字 AND 至少一个大写字母)。

  • ^.*$ 包裹整个正则没用,它只是匹配整行,不解决逻辑组合问题
  • regexp.FindAllString 不能告诉你“缺哪一类”,只返回匹配到的子串
  • 多个 MatchString 调用是可行解法,但要注意:空字符串或全空白符时,strings.TrimSpace 必须前置,否则 " " 会被误判为“含字母”

示例片段:

func checkPassword(s string) bool { s = strings.TrimSpace(s) if len(s) < 8 { return false } hasUpper := regexp.MustCompile(`[A-Z]`).MatchString(s) hasLower := regexp.MustCompile(`[a-z]`).MatchString(s) hasDigit := regexp.MustCompile(`[0-9]`).MatchString(s) hasSpecial := regexp.MustCompile(`[^a-zA-Z0-9\s]`).MatchString(s) return hasUpper && hasLower && hasDigit && hasSpecial }

unicode 包比正则更稳的场景

当你要区分“中文字符”“Emoji”“全角标点”或“控制字符”时,regexp 很难写对(比如 \p{Han} 在 Go 的 regexp 中不支持)。此时直接遍历 rune,用 unicode.IsLetterunicode.IsDigitunicode.IsPunct 等判断更可靠,也更容易调试。

  • unicode.IsLetter(r) 覆盖中日韩英等所有字母,不含数字和下划线
  • unicode.IsPunct(r) 匹配标点,但不包括空格、制表符、换行符(那些归 unicode.IsSpace 管)
  • 遇到 Emoji(如 ?)时,unicode.IsSymbol(r) 才能捕获,IsPunct 会返回 false
  • 不要对每个 rune 都调用 unicode.Is* 四次——先缓存结果,或用位掩码一次收集多类特征

规则引擎不是必须写 DSL:从 map[string]func(string) bool 开始

初学 Go 规则引擎,别一上来就搞 AST 解析或 YACC。密码强度本质是一组独立校验函数的合取(AND),用 map 存规则名和函数,再统一执行即可:

立即学习“go语言免费学习笔记(深入)”;

type Rule struct { Name string Check func(string) bool Msg string } <p>var passwordRules = []Rule{ {"minLength", func(s string) bool { return len(strings.TrimSpace(s)) >= 8 }, "too short"}, {"hasUpper", func(s string) bool { return regexp.MustCompile(<code>[A-Z]</code>).MatchString(s) }, "no uppercase"}, {"hasLower", func(s string) bool { return regexp.MustCompile(<code>[a-z]</code>).MatchString(s) }, "no lowercase"}, {"hasDigit", func(s string) bool { return regexp.MustCompile(<code>[0-9]</code>).MatchString(s) }, "no digit"}, }

  • 规则顺序无关,但错误提示顺序建议按用户感知重要性排(长度 → 字符类型)
  • Check 函数内部别做耗时操作(如 HTTP 请求、文件读取),规则引擎必须低延迟
  • 如果后续要支持“警告级规则”(如“连续重复字符 >3”),可加 Level int 字段区分 error/warn

测试时最容易忽略的边界:空格、BOM、零宽字符

真实用户粘贴密码时,可能带不可见字符:\uFEFF(BOM)、\u200B(零宽空格)、\u00A0(不间断空格)。这些字符会让 len(s)len(strings.TrimSpace(s)) 不一致,导致“明明输够 8 位却提示太短”。

  • 测试用例必须显式包含:"abc123AB\u200B""\uFEFFpassword123""pass word123"(中间有全角空格)
  • 建议在入口统一清理:strings.Map(func(r rune) rune { if unicode.IsControl(r) || unicode.IsMark(r) { return -1 }; return r }, s)
  • unicode.IsMark(r) 能干掉组合字符(如变音符号),避免 “café” 被误判为含非法字符

规则越贴近真实输入,越容易暴露清理逻辑的缺口。别只测干净 ASCII 字符串。