我已经确认电子邮件是唯一的。在注册过程中工作。但是,当我尝试更改用户个人资料中的电子邮件时,出现错误。
我检查电子邮件以运行userRepository.findByEmail。如果找到用户,则电子邮件不是唯一的。但是,当用户在个人资料中更改其电子邮件时,findByEmail会返回该用户。验证失败。
我需要检查findByUser返回的用户是否与更改电子邮件的用户不同。为此,我需要在验证器中传递更改电子邮件的用户。
这是我的代码。
实体:
@Data
@Table(name = "users")
@NoArgsConstructor
@CheckPasswordConfirm(message = "Password not equal!")
@Entity
public class User implements UserDetails{
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@NotBlank(message = "Username is empty!")
@UniqueUsername(message = "Username isn't unique")
private String username;
@NotBlank(message = "Password is empty!")
private String password;
@Transient
@NotBlank(message = "Password confirm is empty!")
private String passwordconfirm;
private boolean active;
@Email(message = "E-mail!")
@NotBlank(message = "E-mail is empty!")
@UniqueEmail(message = "Email isn't unique!")
private String email;
private String activationCode;
@ElementCollection(targetClass = Role.class, fetch = FetchType.EAGER)
@CollectionTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id"))
@Enumerated(EnumType.STRING)
private Set<Role> roles;
public boolean isAdmin()
{
return roles.contains(Role.ADMIN);
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return getRoles();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
AnnotationEmail批注:
@Constraint(validatedBy = UniqueEmailValidator.class)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface UniqueEmail {
public String message();
public Class<?>[] groups() default {};
public Class<? extends Payload>[] payload() default{};
}
验证者:
package ru.watchlist.domain.validation.validators;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import org.springframework.beans.factory.annotation.Autowired;
import ru.watchlist.domain.validation.annotations.UniqueEmail;
import ru.watchlist.service.UserService;
public class UniqueEmailValidator implements ConstraintValidator<UniqueEmail, String> {
@Autowired
private UserService userService;
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return value != null && !userService.isEmailAlredyUse(value);
}
}
userService.isEmailAlredyUse:
public boolean isEmailAlredyUse(String value) {
User user = userRepository.findByEmail(value);
if(user != null) {
return true;
}
return false;
}
在这里我需要检查:
User userFromDB = userRepository.findByEmail(value);
if(userFromDB != null && user != userFromDB) {
return true;
}
我该如何解决这个问题?
P.S。如果我将使用验证器对交叉字段进行分类,则无法在Thymeleaf中显示其字段的错误。因此,我需要字段验证器。
P.S.S。我的控制器:
@Controller
@RequestMapping("/profile")
public class ProfileController {
@Autowired
UserService userService;
@GetMapping
public String profile(@AuthenticationPrincipal User user, Model model) {
model.addAttribute(user);
return "profile";
}
@PostMapping("/update")
public String saveChanges(@Valid User user, Errors errors) {
if(errors.hasErrors()) {
return "profile";
}
userService.addUser(user);
return "redirect:/profile";
}
}
UserRepository:
public interface UserRepository extends JpaRepository<User, Long>{
User findByUsername(String username);
User findByActivationCode(String code);
User findByEmail(String email);
}
答案 0 :(得分:0)
我们不建议使用Hibernate Validator来检查数据库约束。
但是,如果您想这样做,则可以使用约束验证器有效负载https://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/#constraint-validator-payload。它们是为满足此类要求而创建的。
您想要的是示例6.7,因为您不想每次都构建一个完整的ValidatorFactory
,而只是一个特定的上下文。
现在,这是用于Hibernate Validator端的,但是您将需要一种方法来正确配置Spring创建的Validator
以便将已登录的用户注入其中。而且我帮不上你。
答案 1 :(得分:0)
User userFromDB = userRepository.findByEmail(value);
if(userFromDB != null && user != userFromDB) {
return true;
}
在这种情况下,“ user”和“ userFromDB”始终可以不同,因为此对象可以具有不同的hashCode。您可以在返回user
的地方添加存储库和类的实现吗?我假设您没有使用JPA规范,但是如果您使用它,请尝试使用此声明@Column(unique = true)
如果要验证控制器中的对象,可以使用@Valid
批注,其中控制器一侧的代码如下所示:
@PostMapping
public String saveObject(@Valid @ModelAttribute("email") Object object, BindingResult bindingResult) {
if(bindingResult.hasErrors()){
return view_with_form;
}
repository.save(object)
return "redirect:path";
}
从Thymeleaf的形式:
<div class="col-md-6 mt-2 form-group">
<label>Recipe Description:</label>
<input type="text" class="form-control" th:field="*{email}" th:errorclass="is-invalid"/>
<span class="invalid-feedback" th:if="${#fields.hasErrors('email')}">
<ul>
<li th:each="error : ${#fields.errors('email')}" th:text="${error}"></li>
</ul>
</span>
</div>
此示例适用于Bootstrap 4。