Я думаю, у вашего кода есть несколько проблем.
1. Тип 'passwordEncoder'
Существуют различные типы кодировщиков паролей в зависимости от используемого алгоритма кодирования. Если типом «passwordEncoder» является, например, MD5, SHA1, вы, скорее всего, столкнетесь с паролем, поскольку ожидаете, что пароли будут уникальными.
Означает, что у пользователя слабый пароль, например, «topSecret123» и у другого пользователя такой же пароль "topSecret123", метод
oldPasswordsService.findEncryptedPassword(...)
вернет несколько записей вместо одной.
Это приведет, например, к NonUniqueResultException
или чему-то еще .
1.1 Возможное решение:
Связал пароль с именем пользователя. Извлеките пользователя, данного userId (или что-то подобное), и проверьте пароль с помощью пароля этого пользователя.
1.2 Другое возможное решение
Используйте, например, BCryptPasswordEncoder
. Этот тип PasswordEncoder
заботится о добавлении соли в ваш продукт. Это позволяет избежать дублирования записей в вашей базе данных. Эти типы кодировщиков паролей не могут рассчитать пароль или проверить, соответствует ли пароль, если указан только «пароль». Поскольку они используют «соль» с вашим зашифрованным паролем, этим типам кодировщиков паролей требуется (соль + хешированный) пароль в качестве входных данных, чтобы проверить, соответствует ли предоставленный пароль.
2. Актуальная проблема
Код
OldPasswords value = list.get();
- это проблема. Optional<OldPasswords>
может содержать значение null
. Вызов .get()
с Optional
будет null
приведет к java.util.NoSuchElementException: No value present
.
Optional<OldPasswords> list = oldPasswordsService.findEncryptedPassword(passwordEncoder.encode("new password entered form web reset form"));
if (!list.isPresent()) {
return new ResponseEntity<>("The old password value you've entered is incorrect", HttpStatus.UNAUTHORIZED);
}
OldPasswords value = list.get();
boolean matches = passwordEncoder.matches("new password entered form web reset form", value.getEncryptedPassword());
if (matches)
{
return new ResponseEntity<>("PASSWORD_ALREADY_USED", HttpStatus.BAD_REQUEST);
}
else
{
OldPasswords oldPasswords = new OldPasswords();
oldPasswords.setEncryptedPassword(passwordEncoder.encode(resetDTO.getPassword()));
oldPasswordsService.save(oldPasswords);
}
3. Субъекты OldPasswords
Вам не нужно создавать ни @Id
столбец unique=true
, ни nullable=false
нет updateable=false
.
4. Смешивание слоев
Код, который вы разместили, использует службы и обновляет доменные объекты. И он возвращает ResponseEntity
. Вы четко смешиваете разные слои приложения в один.
5. Предоставление информации
Вы предоставляете информацию, что выбранный (новый) пароль уже используется другим пользователем! Не делай этого! Это складывается из-за пункта 1. Я перечислил.
Редактировать:
После того, как вопрос был обновлен, я также хочу обновить свой ответ. Поскольку код, вставленный в обновленный вопрос, не компилируется, я хотел сделать очень простой, базовый c пример, основанный на том, что я знаю из фрагментов кода.
Я не комментирую концепцию Дизайн «сброса пароля», как показано в вопросе, так как в промежутке между ними отсутствует много кода.
Весь код, включая тесты, можно найти здесь: https://github.com/mschallar/so_oldpasswords_example
Код запрашиваемой функции в вопросе:
@PostMapping("reset_password")
public ResponseEntity<?> reset(@RequestBody PasswordResetDTO resetDTO) {
Optional<User> findByLogin = this.userService.findByLogin(resetDTO.getName());
if (!findByLogin.isPresent()) {
return ResponseEntity.notFound().build();
}
User user = findByLogin.get();
Integer userId = user.getUserId();
String encodedPassword = passwordEncoder.encode(resetDTO.getPassword());
for (OldPasswords oldPasswords : oldPasswordsService.findByOwnerId(userId)) {
if (passwordEncoder.matches(resetDTO.getPassword(), oldPasswords.getEncryptedPassword())) {
// Information: Don't do that! Don't reveal that another user already has such a password!
log.info("Password already used.");
return new ResponseEntity<>("PASSWORD_ALREADY_USED", HttpStatus.BAD_REQUEST);
}
}
OldPasswords oldPasswords = new OldPasswords();
oldPasswords.setEncryptedPassword(passwordEncoder.encode(encodedPassword));
oldPasswords.setPasswordOwnerId(userId);
oldPasswordsService.save(oldPasswords);
user.setEncryptedPassword(encodedPassword);
user.setResetPasswordToken(null);
userService.save(user);
return ResponseEntity.ok().build();
}