使用Hibernate @Pattern注解时,如何将哈希密码存入数据库?
解决方案
方法一:使用DTO(数据传输对象)分离校验与持久化逻辑
这是最推荐的经典方案,核心是把前端输入校验和数据库实体的职责彻底分开:
- 创建
UserRegistrationDTO类,专门用于接收注册请求并做格式校验,把原User实体中的密码、邮箱校验注解移到这个DTO上:
public class UserRegistrationDTO { @Pattern(regexp = "[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}", message = "必须是有效的邮箱地址") private String email; @Pattern(regexp = "(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d]{6,}", message = "密码至少6位,必须包含字母和数字") private String password; // 生成getter、setter方法 }
- 控制器接收DTO并完成校验,校验通过后转换为User实体,处理哈希密码再保存:
public String create(@Validated @ModelAttribute("userDTO") UserRegistrationDTO userDTO, BindingResult result) throws EntityNotFoundException { if (result.hasErrors()) { // 校验失败返回注册页面 return "register"; } User user = new User(); user.setEmail(userDTO.getEmail()); user.setPassword(new BCryptPasswordEncoder().encode(userDTO.getPassword())); userService.create(user); return "redirect:/profile"; }
- User实体只保留数据库映射相关注解,移除密码的格式校验:
public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; @Column(name = "email", nullable = false, unique = true) private String email; @Column(name = "password", nullable = false) private String password; // 生成getter、setter方法 }
这种方式既保留了前端输入的格式校验,又避免了哈希后的密码被二次校验。
方法二:禁用JPA自动校验
Spring Data JPA默认会在调用save()时触发Bean Validation,我们可以全局禁用这个行为,只在控制器层做校验:
- 在
application.properties中添加配置:
spring.jpa.properties.javax.persistence.validation.mode=none
- 控制器层依然用
@Validated做前端输入校验,校验通过后直接哈希密码并保存,此时JPA不会再触发实体的校验逻辑。
注意:该配置会全局禁用JPA的自动校验,如果其他实体需要在保存时做校验,需要手动在服务层调用
Validator完成校验。
方法三:使用校验分组
通过Spring Validation的分组功能,让密码的@Pattern注解只在注册校验时生效,保存时不触发:
- 定义一个空的校验分组接口:
public interface RegistrationValidationGroup { }
- 修改User实体的校验注解,指定仅在该分组下生效:
public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; @Pattern(regexp = "[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}", message = "必须是有效的邮箱地址", groups = RegistrationValidationGroup.class) @Column(name = "email", nullable = false, unique = true) private String email; @Pattern(regexp = "(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d]{6,}", message = "密码至少6位,必须包含字母和数字", groups = RegistrationValidationGroup.class) @Column(name = "password", nullable = false) private String password; // 生成getter、setter方法 }
- 控制器层使用
@Validated指定校验分组:
public String create(@Validated(RegistrationValidationGroup.class) @ModelAttribute("user") User user, BindingResult result) throws EntityNotFoundException { if (result.hasErrors()) { return "register"; } user.setPassword(new BCryptPasswordEncoder().encode(user.getPassword())); userService.create(user); return "redirect:/profile"; }
这样只有在控制器指定RegistrationValidationGroup分组时,才会触发密码的格式校验,JPA保存时使用默认分组,不会触发该校验规则。
内容的提问来源于stack exchange,提问作者jackeylove271
相关产品推荐
相关产品推荐

