mysql 8.0+ 原生不支持密码重用限制,官方无「禁止使用最近n次历史密码」机制;仅 validate_password 插件校验强度,不记录/比对历史密码;必须自行建表存 sha2(明文,256) 哈希并由应用层在 alter user 前后显式校验与插入,且无法原子保障一致性。

MySQL 8.0+ 原生不支持密码重用限制
MySQL 本身没有内置的「禁止使用最近 N 次历史密码」机制。官方仅提供 validate_password 插件用于强度校验(如长度、大小写、数字等),但不记录历史密码,也不校验新密码是否曾用过。试图通过配置 validate_password_history 或类似参数会失败——这些是 Oracle MySQL 文档误传或混淆了其他数据库(如 Oracle DB)的术语,MySQL 官方变量列表中并不存在。
必须自行实现历史密码表 + 触发器/应用层拦截
可行路径只有一条:在业务库中建表存历史哈希,每次改密前查表比对。注意三点:
- MySQL 不允许在
BEFORE UPDATE触发器中修改当前用户(mysql.user表受系统保护),所以不能在数据库侧自动拦截ALTER USER ... IDENTIFIED BY -
password_history表必须存储user@host组合 + 密码哈希(建议用SHA2(password, 256),而非明文)+ 时间戳 - 实际拦截动作必须由应用或运维脚本完成:执行
ALTER USER前,先SELECT COUNT(*) FROM password_history WHERE user = ? AND host = ? AND password_hash = SHA2(?, 256),若结果 > 0 则拒绝
ALTER USER 执行后如何安全捕获新密码哈希
MySQL 不提供密码变更事件通知,也没有「密码变更后触发自定义逻辑」的能力。因此无法被动监听。唯一可靠方式是:应用层改密时,显式调用两步操作:
- 第一步:用
ALTER USER 'u'@'h' IDENTIFIED BY 'newpass'更新密码 - 第二步:立即执行
INSERT INTO password_history (user, host, password_hash, changed_at) VALUES ('u', 'h', SHA2('newpass', 256), NOW())
这两步需放在同一事务(如果应用支持)或强顺序保障中;否则可能漏记或记错。切勿依赖 SELECT authentication_string FROM mysql.user 反查——该字段值是内部格式(如 $A$... 前缀的 scrambler hash),与原始密码无确定可逆关系,也无法用于比对重用。
兼容性与权限陷阱
即使你实现了上述逻辑,仍需注意:
- 普通用户默认无权读写
mysql.user,也无权创建跨库表;password_history必须建在业务库,并授予对应用户INSERT/SELECT权限 - MySQL 8.0 默认启用
caching_sha2_password插件,其认证哈希与SHA2(..., 256)不同,不能直接拿来比对;务必统一用应用层计算的SHA2('plain', 256)存储和校验 - 如果用代理(如 ProxySQL)或连接池(如 HikariCP),密码可能被缓存,导致应用认为已更新而实际未生效,此时历史记录和真实密码状态会脱节
真正难的不是建表或写 SQL,而是把「密码变更」这个本应原子的操作,在 MySQL 缺失钩子能力的前提下,硬拆成多步并保证一致性——任何一环异步、失败或绕过,整套机制就失效。











