bcryptpasswordencoder 默认自动加盐,每次encode()生成强随机salt并嵌入结果字符串(如$2a$10$...),无需手动处理;matches()自动解析salt与rounds完成比对。

BCryptPasswordEncoder 为什么默认就加盐,根本不用手动处理
因为 BCryptPasswordEncoder 内部每次调用 encode() 都会自动生成一个强随机 salt,并和 hash 结果一起编码进最终字符串里——格式是 $2a$10$... 这种,其中 $2a$ 是算法标识,10 是 log rounds,后面跟着的 base64 字符串里已经混入了 salt 和 hash。你不需要、也不该自己拼接 salt 或提前生成。
常见错误现象:
• 手动用 SecureRandom 生成 salt 再传给 BCryptPasswordEncoder(它不接受外部 salt 参数)
• 把 encode 出来的字符串拆开取 salt 单独存库(完全没必要,也破坏了校验逻辑)
• 认为“没看到显式 salt 变量 = 没加盐”(错,salt 就藏在返回值里)
-
BCryptPasswordEncoder的构造函数可传入strength(默认 10),值越大越慢但越安全,生产环境建议 10–12 - 不同 strength 产生的 hash 字符串长度一致,但前缀
$2a$10$里的数字会变,校验时自动识别,无需额外存储参数 - Spring Security 5.0+ 默认使用
DelegatingPasswordEncoder,但如果你直接 newBCryptPasswordEncoder,就只走 bcrypt 流程,无兼容层开销
passwordEncoder.matches() 怎么自动提取 salt 并比对
调用 matches() 时,它会从已存储的 hash 字符串(比如数据库里查出的 $2a$10$abc123...)里解析出 algorithm、strength 和 salt,再用同样的 salt + rounds 对明文重新哈希,最后字节比对。整个过程全自动,你只管传两个字符串。
容易踩的坑:
• 把数据库字段设成 VARCHAR(60) —— 实际 bcrypt 输出最长可达 60 字符,但某些旧实现或迁移数据可能略长,建议留到 VARCHAR(72)
• 在校验前对用户输入 trim() 或转小写,而存库时没做同样处理(导致 hash 不匹配,且很难排查)
立即学习“Java免费学习笔记(深入)”;
- 明文密码传入
matches()前,必须和当初encode()时的原始形态完全一致(包括空格、换行、编码) - 如果数据库里存的是带前缀的完整 hash 字符串,就直接传进去;别试图截掉
$2a$10$前缀再比对 - 校验失败不会抛异常,只返回
false,别误以为是代码卡住或网络问题
为什么不能把 BCryptPasswordEncoder 当通用哈希工具用
BCryptPasswordEncoder 是专为密码设计的:慢、带盐、抗 GPU 暴力。它不适用于需要快速计算、可逆、或和其他系统互通哈希值的场景(比如签名、缓存 key、文件摘要)。
典型误用场景:
• 用它计算 API 请求签名(性能差,结果不可预测,其他语言难对齐)
• 存 token 或 session ID 的哈希值(没必要抗碰撞,反而拖慢高频操作)
• 替代 MessageDigest.getInstance("SHA-256") 做数据指纹(输出格式不标准,无法跨平台验证)
- bcrypt 输出不是纯 hex,而是
crypt(3)兼容的 base64 变种(用./而非+/),和其他 base64 解码器不直接兼容 - 没有“验证 salt 是否合法”的公开方法,它的 API 就是 encode/matches 两条路,别想绕过
- 如果项目需要多算法支持(如迁移老 MD5 用户),得用
DelegatingPasswordEncoder,而不是硬切BCryptPasswordEncoder
Spring Boot 中配置 BCryptPasswordEncoder 的实际要点
Spring Boot 2.0+ 默认自动配置一个 BCryptPasswordEncoder bean,strength=10。如果你没手动定义同类型 bean,就能直接 @Autowired 使用。但要注意几个隐性行为:
- 测试环境有时会覆盖为
NoOpPasswordEncoder(尤其在未引入spring-boot-starter-security时),检查日志里有没有 “Using generated security password” 提示 - 自定义 bean 时别漏掉
@Bean注解,否则 Spring 不会注入,运行时报No qualifying bean - 如果用了
@ConfigurationProperties动态配 strength,要确保配置项存在且类型正确(int,不能是字符串)
示例配置方式:@Beanpublic PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(12);}
真正麻烦的不是怎么写这三行,而是上线后发现所有老用户登不上——因为改了 strength,而 matches() 仍能向下兼容旧 hash,但新注册用户全用更高 cost,运维查日志时容易忽略这个渐进变化。











