应避免单用正则校验密码强度,因其易漏弱口令、难控长度与unicode问题;推荐拆分检查项、用unicode包替代正则、结合zxcvbn-go评估真实强度,并将策略配置化、校验逻辑下沉至domain层。

为什么 regexp 单独校验密码强度容易出错
直接用正则匹配“至少一个大写字母、一个小写、一个数字、一个特殊字符”看似简洁,但实际会漏掉常见弱口令(如 "Password123!"),也难以控制长度下限与上限、禁止连续重复字符或字典词。更关键的是,regexp.MatchString 对 Unicode 处理不一致,比如用户输入中文标点或全角数字时,\d 无法匹配。
实操建议:
- 不要只依赖单个正则;拆成多个独立检查项,每项可单独开关、加权重或记录具体失败原因
- 用
unicode.IsUpper、unicode.IsDigit等函数替代\p{Lu}类正则,避免编码歧义 - 对输入先做
strings.TrimSpace,防止空格绕过长度校验 - 若需禁用常见弱口令,优先查本地小规模黑名单(如 top10000.txt),而非调用外部 API——网络延迟和失败会阻塞注册流程
如何用 zxcvbn-go 做真实强度评估而非规则堆砌
zxcvbn-go 是 Dropbox zxcvbn 的 Go 移植版,它不靠硬编码规则,而是基于模式识别(如日期、键盘路径、常见单词变体)估算破解耗时,返回 Score(0–4)和详细反馈(Feedback)。这对 UX 更友好:不是简单报“密码太弱”,而是提示“请避免使用生日或连续字母”。
实操建议:
- 安装:
go get github.com/sonnt85/zxcvbn-go - 调用时传入原始字符串,不要预处理(如转小写),否则影响字典匹配准确性
- 注意其默认字典不含中文,如需支持,得自行注入拼音或常用中文密码词表(通过
zxcvbn.WithAdditionalGazetteer) - 性能上,单次评估约 0.5–3ms,高并发注册场景建议加缓存(如用
sync.Map缓存前缀哈希 + score),但注意别缓存明文密码
密码策略配置该存在哪?硬编码还是配置文件
把最小长度、是否允许重复字符、特殊字符白名单等写死在代码里,会导致每次策略调整都要发版。但全扔进 JSON 配置又可能引发类型错误或缺失字段 panic。
实操建议:
- 定义结构体承载策略,例如:
type PasswordPolicy struct { MinLength int `json:"min_length"` MaxLength int `json:"max_length"` RequireUpper bool `json:"require_upper"` SpecialChars string `json:"special_chars"` // 如 "!@#$%^&*" DisallowSequences bool `json:"disallow_sequences"` } - 用
viper或原生json.Unmarshal加载,但务必在初始化时调用Validate()方法校验值合理性(如MinLength > MaxLength应 panic) - 敏感项(如黑名单路径)不要放环境变量,避免被
ps aux泄露;改用文件读取 +os.ReadFile,并确保文件权限为0600
校验时机选在 HTTP handler 还是 domain 层
如果只在 POST /register 的 handler 里做校验,后续其他入口(如管理员后台批量导入、CLI 工具重置密码)就得重复逻辑,且无法复用单元测试。
实操建议:
- 把密码校验封装为纯函数,放在 domain 或 pkg/password 下,接收
string和*PasswordPolicy,返回error或PasswordStrength结构 - HTTP handler 只负责解析参数、调用该函数、映射 error 到 HTTP 状态码(如
password.ErrTooWeak→400) - 特别注意:数据库写入前必须再次校验——中间件或 handler 的校验可能被绕过(如直接调用 DAO 层)
if 判断,而是让策略能随业务演进平滑调整:比如某天要求禁止手机号片段,你得能快速加一条规则而不重构整个校验链;又比如导出密码强度统计报表时,需要每个失败项有明确标识字段,而不是只丢一个模糊的 "invalid password"。这些细节,往往在第一次设计接口时就被忽略。











