我有一个 UserController,它接收 UserDTO 并在数据库中创建/更新用户。我遇到的问题是,我也有一个登录名,当我在登录表单上插入用户名和密码时,我总是得到“密码错误”。尽管正确插入了凭据,但还是有例外。
我怀疑的一件事是 BCrypt 应该受到指责,因为由于它在编码时生成随机盐,也许,只是也许,密文最终会有所不同之类的东西,这很奇怪,因为我认为它应该起作用。我想知道如何解决散列不同且无法验证 userCredentials 的问题
我尝试过对收到的密码进行编码并通过我的自动连线密码编码器使用 matches 方法,并且我正在使用我自己的 authProvider。
这是代码,如果您需要其他内容,请告诉我。
CustomAuthProvider.java
@Service
public class CustomAuthProvider implements AuthenticationProvider {
private final UserServiceImpl userServiceImpl;
private final BCryptPasswordEncoder passwordEncoder;
@Autowired
public CustomAuthProvider(UserServiceImpl userServiceImpl, BCryptPasswordEncoder passwordEncoder) {
this.userServiceImpl = userServiceImpl;
this.passwordEncoder = passwordEncoder;
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = authentication.getCredentials().toString();
UserDetails userDetails = userServiceImpl.loadUserByUsername(username);
if (!passwordEncoder.matches(password, userDetails.getPassword())) { //The problem is here evidently.
throw new BadCredentialsException("Wrong password.");
}
return new UsernamePasswordAuthenticationToken(userDetails, password, userDetails.getAuthorities());
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
此外,这是 loadUserByUsername 方法:
UserServiceImpl.java
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserDTO user = this.getUserByUsername(username);
User anUser = convertToUser(user);
ModelMapper modelMapper = new ModelMapper();
return modelMapper.map(anUser,UserPrincipal.class);
}
}
这是我用来保存和更新用户的 save 方法,以及 LoginController;
@Override
public void save(UserDTO user) {
User aUser = this.convertToUser(user);
aUser.setPassword(passwordEncoder.encode(aUser.getPassword()));
this.userRepository.save(aUser); }
LoginController.java:
@RestController
public class LoginController{
private final CustomAuthProvider providerManager;
@Autowired
public LoginController(CustomAuthProvider providerManager) {
this.providerManager = providerManager;
}
@GetMapping("/login")
public String login() {
return "login";
}
@PostMapping("/login")
public String login(@RequestParam("username") @NotBlank String username,
@RequestParam("password") @NotBlank String password, Model model) {
if(username == null || password == null) { //This is probably not necessary
model.addAttribute("error", "Invalid credentials");
return "login";
}
try {
Authentication auth = providerManager.authenticate(
new UsernamePasswordAuthenticationToken(username, password)
);
SecurityContextHolder.getContext().setAuthentication(auth);
return "redirect:/notes";
} catch (AuthenticationException e) {
model.addAttribute("error", "Invalid credentials");
return "login";
}
}
}
UserPrincipal.java
@Data
public class UserPrincipal implements Serializable , UserDetails {
int id;
private String username;
private String password;
private Date accountCreationDate = new Date();
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
public boolean isAccountNonExpired() {
return false;
}
@Override
public boolean isAccountNonLocked() {
return false;
}
@Override
public boolean isCredentialsNonExpired() {
return false;
}
@Override
public boolean isEnabled() {
return false;
}
}
UserDTO.java
@Data
public class UserDTO implements Serializable {
int id;
private String username;
private String password;
private List<Note> notes = new ArrayList<>();
}
我阅读了与该主题相关的几个问题,例如
Spring Boot PasswordEncoder.matches 始终为 false
Spring Security - BcryptPasswordEncoder
与 Spring Boot BCryptPasswordEncoder matches() 方法的哈希值不一致
在 Spring Security 中解码 Bcrypt 编码的密码以停用用户帐户
但这些都没有帮助我解决我的问题,并且没有真正的解决方案,因为他们中的大多数甚至没有公认的答案。
编辑:发现“匹配”方法仅在我插入哈希密码而不是原始密码时才有效。
发现我的错误:
User 类中的 setPassword 方法重新散列已在 save 方法上散列的散列密码,因此 modelMapper.map() 方法使用该 setPassword 方法,因此密码永远不会匹配,并且我从用户类别与我在数据库中看到的实际密码不匹配。
感谢您回来并编写解决方案。我犯了同样的错误,这对我有帮助。