如何用Go语言规则引擎实现Golang密码强度检测器的初级实战?
- 内容介绍
- 文章标签
- 相关推荐
本文共计1034个文字,预计阅读时间需要5分钟。
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.IsLetter、unicode.IsDigit、unicode.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 的 `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.IsLetter、unicode.IsDigit、unicode.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 字符串。

