You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

@Pattern注解校验异常:校验哈希密码而非用户原密码

解决密码校验失效问题

问题根源在于:JPA的Bean Validation(比如@Pattern)默认在实体持久化到数据库前触发校验,但你在保存前已经把原密码替换成了哈希值,导致校验的是符合规则的哈希密码,而非用户输入的原密码。以下是两种实用的解决方法:

方法一:哈希前手动触发校验

直接在AuthService中注入Validator,在对密码哈希之前,先校验原始的User对象,确保原密码符合规则:

@Service
@RequiredArgsConstructor
public class AuthService {

    private final UserService userService;
    private final EmailVerificationService emailVerificationService;
    private final Validator validator; // 注入Spring提供的Validator

    public User registerUser (User user, PasswordEncoder passwordEncoder) throws Exception {
        // 校验原始用户数据(包含原密码)
        Set<ConstraintViolation<User>> violations = validator.validate(user);
        if (!violations.isEmpty()) {
            throw new ConstraintViolationException(violations); // 抛出校验异常,返回错误信息
        }

        // 校验通过后再哈希密码
        String hashPassword = passwordEncoder.encode(user.getPassword());
        user.setPassword(hashPassword);
        
        Authority authority = new Authority();
        authority.setName(SecurityConstants.ROLE_USER);
        authority.setUser(user);
        Set<Authority> authorities = new HashSet<>();
        authorities.add(authority);
        user.setAuthorities(authorities);
        
        //emailVerificationService.verifyEmail(user.getUsername());
        userService.createUser(user);

        return user;
    }
}

方法二:使用DTO做请求参数校验(推荐)

创建一个专门用于接收注册请求的DTO类,把校验注解放在DTO的密码字段上,与数据库实体User分离。这样前端传入的参数会先在Controller层完成校验,再转换为User实体进行哈希和保存:

1. 创建UserRegistrationDTO

public class UserRegistrationDTO {
    private String firstname;
    private String lastname;

    @Pattern(regexp = EmailConstants.REGEX_PATTERN, message = EmailConstants.PATTERN_DOES_NOT_MATCH_MESSAGE)
    private String username;

    @Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9]).{8,}$", message = "try more secure password")
    private String password;

    // 生成getter和setter
}

2. 修改AuthService接收DTO

@Service
@RequiredArgsConstructor
public class AuthService {

    private final UserService userService;
    private final EmailVerificationService emailVerificationService;

    public User registerUser (UserRegistrationDTO dto, PasswordEncoder passwordEncoder) throws Exception {
        // 转换DTO为User实体
        User user = new User();
        user.setFirstname(dto.getFirstname());
        user.setLastname(dto.getLastname());
        user.setUsername(dto.getUsername());
        
        // 哈希DTO中的原密码
        String hashPassword = passwordEncoder.encode(dto.getPassword());
        user.setPassword(hashPassword);

        Authority authority = new Authority();
        authority.setName(SecurityConstants.ROLE_USER);
        authority.setUser(user);
        Set<Authority> authorities = new HashSet<>();
        authorities.add(authority);
        user.setAuthorities(authorities);
        
        //emailVerificationService.verifyEmail(user.getUsername());
        userService.createUser(user);

        return user;
    }
}

3. Controller层添加@Valid触发校验

@PostMapping("/register")
public ResponseEntity<User> register(@Valid @RequestBody UserRegistrationDTO dto, PasswordEncoder passwordEncoder) throws Exception {
    User user = authService.registerUser(dto, passwordEncoder);
    return ResponseEntity.ok(user);
}

方法对比

  • 方法一:无需新增类,快速修复现有代码,适合小型项目或临时修复。
  • 方法二:遵循MVC分层原则,将请求参数校验与数据库实体分离,代码更易维护,推荐用于中大型项目。

内容的提问来源于stack exchange,提问作者Adem Galitekin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 23:47:12