文档站点
Skip to content

密码哈希 ​

Laravel 用 Hash::make() / Hash::check(),默认 bcrypt;Spring Security 用 PasswordEncoder,默认 BCryptPasswordEncoder。接口一致,只有一个要点:永远不要自己实现加密,用 BCrypt 这类自适应哈希。

基本用法 ​

java
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

PasswordEncoder encoder = new BCryptPasswordEncoder();

String hash = encoder.encode("myPassword123");      // 加密(Hash::make)
boolean ok   = encoder.matches("myPassword123", hash);   // 校验(Hash::check)

TIP

Spring Security 有一个神器的 DelegatingPasswordEncoder:同一个字段可以兼容不同算法(旧数据用 MD5,新数据用 bcrypt),默认格式 {bcrypt}$2a$10$...。所以看 Spring 的密码存储有时带 {bcrypt} 前缀,这是故意的,别删。

注册时存哈希 ​

java
@Service
public class UserService {
    private final UserRepository userRepository;
    private final PasswordEncoder passwordEncoder;

    public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder) {
        this.userRepository = userRepository;
        this.passwordEncoder = passwordEncoder;
    }

    public User register(RegisterRequest req) {
        User user = new User();
        user.setUsername(req.getUsername());
        user.setPassword(passwordEncoder.encode(req.getPassword()));   // 只存哈希
        return userRepository.save(user);
    }

    public boolean verify(String username, String rawPassword) {
        return userRepository.findByUsername(username)
                .map(u -> passwordEncoder.matches(rawPassword, u.getPassword()))  // Hash::check
                .orElse(false);
    }
}

为什么不建议自己写加密 ​

  • MD5/SHA1:几十年前的算法,GPU 爆破以秒计
  • 自己发明算法:几乎必然有漏洞
  • 普通 SHA-256:无盐可加盐,但快,适合暴力破解

正确选择(和 Laravel 官方建议一致):

算法用途
BCrypt(默认)存密码,首选
Argon2(Argon2PasswordEncoder)更强,新项目可考虑
PBKDF2遗留兼容

BCrypt 的「成本因子」strength 默认 10,可调:

java
@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder(12);   // 强度越高越慢越安全
}

为什么 BCrypt 慢是好事?

BCrypt 故意设计成「计算慢」(约 100ms 一次),让暴力破解成本成指数上升。Laravel 的 Hash::make 默认 bcrypt 同理。加密库的「慢」是特性,不是性能缺陷。但注意别在循环里对每个用户跑 encode,会很卡。

密码校验流程(认证时的标准操作) ​

用户提交明文密码
  → 查库拿哈希
  → passwordEncoder.matches(明文, 哈希)   // 比对
  → 返回 true/false

在 认证 的 JWT 例子里,authManager.authenticate() 内部就是走这条流程,PasswordEncoder 就配置在容器里,自动参与。

实践清单 ​

  1. 数据库里永远只存哈希,绝不存明文(即使写 demo 也不行)
  2. 密码字段用 @JsonIgnore 或 DTO 隔离,绝不进响应体(见 响应)
  3. 加密成本调合理值:开发环境 10,生产按硬件实测调 12~14
  4. 登录接口记得配合 限流,防爆破
  5. 别用「可逆加密」存密码 —— 可逆 = 可破解,永远不可逆

面向 PHP / Laravel 开发者的 Spring Boot 中文文档